语言设计。在Haskell中,解析表达式的评估是如何进行的?[非公开]

问题描述 投票:1回答:1

我正在用 Haskell 编写一个简单的语言,但我在评估算术和关系表达式时遇到了一个问题。一开始我写了一个评估函数,然后我认识到我需要单独的函数来评估我的语言的每个方面。所以我有一个算术评估函数,一个关系评估函数,然后是一个通用评估函数。

问题出在实际产生一些工作上,我认为这来自于不能将我的其他评估函数与我的主评估函数联系起来。然而我不知道我的评估方法是否不正确。我相信也许像'foldl1'这样的函数在评估中实现会更有用。我第一次了解到这个函数是在《48小时内给自己写一个方案》Wikibook中,它似乎比评估每个可能的表达式要有用得多。我不确定这在我目前的代码中如何实现,除非我有一个函数来处理每个表达式。例如,如果我有一个像 "1 + 1 "这样的表达式,我目前的解析器将其解析为 "1 Add 1",但如果我使用 foldl1,我可以有一个函数来获取这个表达式的每个部分,并在参数上 "折叠 "运算符。即使我将我目前的评估函数与 foldl1 的想法合并,我仍然可以预见在评估任何有意义的东西时存在问题,而不仅仅是一个整数或字符串。

任何方向或帮助都将被感激。

以下是我的数据类型和我的评估函数。

data HenryVal = Atom String
              | String String 
              | Integer Integer
              | Bool Bool
              | Not HenryVal
              | Neg HenryVal
              | List [HenryVal]
              | Seq [HenryVal]
              | Assign String HenryVal
              | If HenryVal HenryVal HenryVal
              | While HenryVal HenryVal
              | Skip
              | ABinary ABinOp HenryVal HenryVal
              | BBinary BBinOp HenryVal HenryVal
              | RBinary RBinOp HenryVal HenryVal

data BBinOp = And | Or deriving (Show)
data RBinOp = Greater | Less deriving (Show)
data ABinOp = Add
            | Subtract
            | Multiply
            | Divide
              deriving (Show)

evalABinOP :: HenryVal -> ABinOp -> HenryVal -> HenryVal
evalABinOP (Integer a) Add (Integer b) = Integer (a + b)
evalABinOP (Integer a) Multiply (Integer b) = Integer (a * b)
evalABinOP (Integer a) Divide (Integer b) = Integer (a `div` b)
evalABinOP (Integer a) Subtract (Integer b) = Integer (a - b)

evalRBinOp :: HenryVal -> RBinOp -> HenryVal -> HenryVal
evalRBinOp (Integer a) Greater (Integer b) = if a > b then (Bool True) else (Bool False)
evalRBinOp (Integer a) Less (Integer b) = if a < b then (Bool True) else (Bool False)

evalStmt :: HenryVal -> [HenryVal]
evalStmt (Assign var val) = [val]

evalCond :: HenryVal -> Bool
evalCond (Bool cond) = if cond == True then True else False


eval :: HenryVal -> HenryVal
eval val@(Atom _) = val
eval val@(String _) = val
eval val@(Integer _) = val
eval val@(Bool _) = val
eval val@(Neg _) = val
eval val@(Not _) = val
eval (List [Atom "quote", val]) = val
eval val@(List _) = val
eval val@(Seq _) = val
eval (If cond a b) = if (evalCond cond) then (eval a) else (eval b) 
eval (Assign var val) = eval val
eval (Seq (Atom func : args)) = apply func $ map eval args
eval (ABinary op x y) = evalABinOP x op y
eval (RBinary op x y) = evalRBinOp x op y

错误信息:

./hask "[4 + 4]"
"No match: "Henry" (line 1, column 4):
unexpected "+"
expecting space, "(", "if", ";Do", "skip", identifier, letter, digit, "\"" or "[""

在48小时内给自己写一个方案。https:/en.wikibooks.orgwikiWrite_Yourself_a_Scheme_in_48_HoursEvaluation,_Part_1。

parsing haskell functional-programming evaluation language
1个回答
5
投票

我不确定使用 foldl1 在这里会对你有很大的帮助。 在 "给自己写一个方案 "的维基书中。foldl1 函数用于应用 "二进制 "函数的特定目的,如 +- 到可能是长度为&gt的参数列表;2.在Lisp中。(+ 1 2 3) 和Haskell表达式的意思一样。

foldl1 (+) [1,2,3]

所以对于这个 特定 目的: foldl1 是一种很好的评估Lisp算术表达式的方法。 如果你有一个 HenryVal 构造函数,看起来像 ABinary ABinOp [HenryVal] 而你想让你的 列表 的参数,那么你可能会有用武之地。foldl1.

那么,我们如何解决你遇到的问题。 如果我可以猜测的话,我想你可能在看一个类似:

ABinary Add (ABinary Add (Integer 1) (Integer 2)) (Integer 3)

的表达式,然后意识到你的评估器无法处理它,因为 evalABinOp 没有任何情况下,当其中一个参数是另一个 ABinary 而非 Integer对吗?

嗯,诀窍是使用递归。 在 evalABinOp,递归地评估参数,确保它们是整数,并且 然后 做算术。 所以,类似于。

evalABinOp :: HenryVal -> ABinOp -> HenryVal -> HenryVal
evalABinOp e1 op e2
  = let Integer v1 = eval e1
        Integer v2 = eval e2
    in Integer $ calc v1 op v2
  where
    calc a Add b = a + b
    calc a Multiply b = a * b
    calc a Divide b = a `div` b
    calc a Subtract b = a - b

然后,在加上一个 deriving (Show) 例子 HenryVal,你可以看到E。

> eval $ ABinary Add (ABinary Add (Integer 1) (Integer 2)) (Integer 3)
Integer 6

作为一种选择 我突然想到,你可以保留你原来的定义。evalABinOp,原样。

evalABinOp :: HenryVal -> ABinOp -> HenryVal -> HenryVal
evalABinOp (Integer a) Add (Integer b) = Integer (a + b)
evalABinOp (Integer a) Multiply (Integer b) = Integer (a * b)
evalABinOp (Integer a) Divide (Integer b) = Integer (a `div` b)
evalABinOp (Integer a) Subtract (Integer b) = Integer (a - b)

并修改相应的 eval 的情况下,使其递归。

eval (ABinary op x y) = evalABinOp (eval x) op (eval y)

这将产生同样的效果。

© www.soinside.com 2019 - 2024. All rights reserved.