Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Parsing 语言设计:在Haskell中解析表达式的计算是如何工作的?_Parsing_Haskell_Functional Programming_Evaluation - Fatal编程技术网

Parsing 语言设计:在Haskell中解析表达式的计算是如何工作的?

Parsing 语言设计:在Haskell中解析表达式的计算是如何工作的?,parsing,haskell,functional-programming,evaluation,Parsing,Haskell,Functional Programming,Evaluation,我正在用Haskell编写一种简单的语言,但在计算算术和关系表达式时遇到了一个问题。起初我写了一个评估函数,然后我意识到我需要单独的函数来评估我语言的各个方面。我有一个算术求值函数,一个关系求值函数,然后是一般求值函数 这个问题产生于实际生成一些要处理的东西,我认为这是因为无法将我的其他eval函数与我的主要eval函数联系起来。但是,我不确定我是否正确地进行了评估。我相信,也许像“foldl1”这样的函数更适合在评估中实现。我是在48小时的维基百科中从Write Yourself A Sche

我正在用Haskell编写一种简单的语言,但在计算算术和关系表达式时遇到了一个问题。起初我写了一个评估函数,然后我意识到我需要单独的函数来评估我语言的各个方面。我有一个算术求值函数,一个关系求值函数,然后是一般求值函数

这个问题产生于实际生成一些要处理的东西,我认为这是因为无法将我的其他eval函数与我的主要eval函数联系起来。但是,我不确定我是否正确地进行了评估。我相信,也许像“foldl1”这样的函数更适合在评估中实现。我是在48小时的维基百科中从Write Yourself A Scheme中学到这个函数的,它似乎比计算每个可能的表达式更有用。除非每个表达式都有一个函数,否则我不确定这在我当前的代码中是如何实现的。例如,如果我有一个类似“1+1”的表达式,我当前的解析器将其解析为“1添加1”,但如果我使用foldl1,我可以使用一个函数来获取该表达式的每个部分,并在参数上“折叠”运算符。即使我将我当前的求值函数与foldl1的思想合并,我仍然预见到了从中求值任何有意义的东西的问题,而不仅仅是一个整数或字符串

任何指导或帮助都将不胜感激

以下我已通过datatypse和我的评估功能包括:

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

在48小时内给自己写一个计划:

我不确定使用
foldl1
会对你有多大帮助。在“为自己编写方案”wikibook中,
foldl1
函数用于将“二进制”函数,如
+
-
应用于长度可能大于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
而不是
整数,对吗

好的,诀窍是使用递归。在
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
然后,在将
派生(Show)
实例添加到
HenryVal
之后,您可以看到:

> 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)

这也会有同样的效果。

在我看来,你做得很好。您已经成功地将专门的评估函数与主评估函数关联起来。你在找什么帮助?我不认为foldl1能神奇地解决你的问题-也许一些更一般的概念,比如一个变形,会有所帮助,但它似乎有点棘手,因为你的列表构造函数需要特殊的大小写,cata的要点是它为你做了所有的递归。我想我不明白的是,因为没有更好的术语,所以让它做一些事情。我不知道如何将任意表达式与求值函数联系起来,得到我想要的结果。例如,我可以声明一个数字,但如果对该数字进行平方运算,则会产生一个错误,表明运算符是意外的。请包含导致错误的确切输入以及错误本身,以便我们可以重现您的问题。您发布的错误消息是由解析器生成的,而不是由求值函数生成的。因此,您的解析器代码中有一些东西不希望在此位置出现
+
符号。您添加了错误,但肯定不是生成错误的代码。这非常需要一个——就目前而言,它是无法回答的。旁白:我认为将参数重新排序到
calc
Integer$calc op v1 v2,其中calc Add=(+);calc Multiply=(*)…
这无疑解决了涉及算术的问题。如果我能问的话,你的evalABinOp和我的evalABinOp有很大区别吗?我假设你的不同之处在于递归地计算参数,而我的只是假设有两个整数被运算。在你的评论之后,虽然是解析器导致了问题,而不是严格意义上的计算器…我想我需要回去看看我的解析器哪里出了故障。不,没有很大的区别。他们很相似。事实上,我添加了一个替代解决方案,它具有相同的效果,但保持您的
evalABinOp
不变。听到这个消息太好了!不过,我可能会把它改成你的,因为它更清晰,至少对我来说,关于实际发生的事情。
eval (ABinary op x y) = evalABinOp (eval x) op (eval y)