首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >是否有更易于维护的方法来处理我的数据类型?

是否有更易于维护的方法来处理我的数据类型?
EN

Stack Overflow用户
提问于 2018-03-13 01:42:20
回答 1查看 105关注 0票数 2

我使用以下数据类型定义了递归下降解析器的结果:

代码语言:javascript
复制
data CST 
    = Program CST CST
    | Block CST CST CST 
    | StatementList CST CST
    | EmptyStatementList
    | Statement CST
    | PrintStatement CST CST CST CST
    | AssignmentStatement CST CST CST
    | VarDecl CST CST
    | WhileStatement CST CST CST 
    | IfStatement CST CST CST 
    | Expr CST
    | IntExpr1 CST CST CST 
    | IntExpr2 CST
    | StringExpr CST CST CST
    | BooleanExpr1 CST CST CST CST CST
    | BooleanExpr2 CST 
    | Id CST
    | CharList CST CST 
    | EmptyCharList
    | Type CST 
    | Character CST
    | Space CST
    | Digit CST
    | BoolOp CST
    | BoolVal CST
    | IntOp CST
    | TermComponent Token
    | ErrorTermComponent (Token, Int)
    | NoInput

正如数据类型名称所暗示的那样,数据类型构建了一个具体的语法树。我想知道是否有一种比这种类型更易于维护的模式匹配方法。例如,要跟踪解析调用的执行情况,我有以下内容:

代码语言:javascript
复制
checkAndPrintParse :: CST -> IO ()
checkAndPrintParse (Program c1 c2) = do
    putStrLn "Parser: parseProgram" 
    checkAndPrintParse c1
    checkAndPrintParse c2
checkAndPrintParse (Block c1 c2 c3) = do
    putStrLn "Parser: parseBlock"
    checkAndPrintParse c1
    checkAndPrintParse c2
    checkAndPrintParse c3
checkAndPrintParse (StatementList c1 c2) = do
    putStrLn "Parser: parseStatementList"
    checkAndPrintParse c1
    checkAndPrintParse c2

诸若此类。我已经研究过fix函数/模式,但我不确定它在这里是否适用。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-03-13 02:51:01

使用泛型-派生获取构造函数的名称:

  • 派生Generic (从GHC.Generics)
  • 调用conNameOf :: CSTF -> String (从Generics.Deriving)

使用递归-方案遍历递归类型:

  • makeBaseFunctor派生递归类型的基函子。CST的基函子称为CSTF,它是一个参数化类型,形状与CST相同,但CST的递归出现被替换为类型参数。
  • 学会使用cata (在开始的时候,这可能有点令人费解)。在这种情况下,我们希望从一个IO () (即函数CST -> IO () )递归构造一个CST -> IO ()操作。为此,cata类型变为(CSTF (IO ()) -> IO ()) -> CST -> IO () (带有t ~ CSTa ~ IO ()),其中第一个参数定义结果递归函数的主体,递归调用的结果放在基函子的字段中。

因此,如果您的目标是编写一个递归函数checkAndPrintParse,其情况如下:

代码语言:javascript
复制
checkAndPrintParse (Program c1 c2) = do
  putStrLn "Parser: parseProgram" 
  checkAndPrintParse c1
  checkAndPrintParse c2

cata将把它对c1c2的递归调用的结果替换为以下字段:

代码语言:javascript
复制
-- goal: find f such that   cata f = checkAndPrintParse

-- By definition of cata
cata f (Program c1 c2) = f (ProgramF (cata f c1) (cata f c2))

-- By the goal and the definition of checkAndPrintParse
cata f (Program c1 c2) = checkAndPrintParse (Program c1 c2) = do
  putStrLn "Parser: parseProgram" 
  checkAndPrintParse c1
  checkAndPrintParse c2

因此

代码语言:javascript
复制
f (ProgramF (cata f c1) (cata f c2)) = do
  putStrLn "Parser: parseProgram"
  cata f c1
  cata f c2

抽象cata f c1cata f c2

代码语言:javascript
复制
f (ProgramF x1 x2) = do
  putStrLn "Parser: parserProgram"
  x1 >> x2

识别一个褶皱(在Foldable意义上)

代码语言:javascript
复制
f t@(ProgramF _ _) = do
  putStrLn "Parser: parserProgram"
  sequence_ t

再概括

代码语言:javascript
复制
f t = do
  putStrLn $ "Parser: " ++ conNameOf t  -- Prints "ProgramF" instead of "parserProgram"... *shrugs*
  sequence_ t

这就是我们给cata的论点。

代码语言:javascript
复制
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}

import GHC.Generics
import Generics.Deriving (conNameOf)
import Data.Functor.Foldable
import Data.Functor.Foldable.TH (makeBaseFunctor)

data CST 
    = Program CST CST
    | Block CST CST CST 
    | StatementList CST CST
    | EmptyStatementList
    | Statement CST
    | PrintStatement CST CST CST CST
    | AssignmentStatement CST CST CST
    | VarDecl CST CST
    | WhileStatement CST CST CST 
    | IfStatement CST CST CST 
    | Expr CST
    | IntExpr1 CST CST CST 
    | IntExpr2 CST
    | StringExpr CST CST CST
    | BooleanExpr1 CST CST CST CST CST
    | BooleanExpr2 CST 
    | Id CST
    | CharList CST CST 
    | EmptyCharList
    | Type CST 
    | Character CST
    | Space CST
    | Digit CST
    | BoolOp CST
    | BoolVal CST
    | IntOp CST
    | TermComponent Token
    | ErrorTermComponent (Token, Int)
    | NoInput
    deriving Generic

data Token = Token

makeBaseFunctor ''CST

deriving instance Generic (CSTF a)

checkAndPrintParse :: CST -> IO ()
checkAndPrintParse = cata $ \t -> do
  putStrLn $ "Parser: " ++ conNameOf t
  sequence_ t

main = checkAndPrintParse $
  Program (Block NoInput NoInput NoInput) (Id NoInput)

输出:

代码语言:javascript
复制
Parser: ProgramF
Parser: BlockF
Parser: NoInputF
Parser: NoInputF
Parser: NoInputF
Parser: IdF
Parser: NoInputF
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49247047

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档