在这里,我尝试使用Exp
数据类型的定义值来评估Haskell中的表达式。函数类型为eval :: Exp -> Int
,数据类型为:
data Exp = Num Int
| Exp :+: Exp
| Exp :-: Exp
| Exp :*: Exp
| Exp :/: Exp
deriving (Show)
和我要评估的值是:
4 + 5 * 6 , respecting correct precedences of the operators
4 + 5 + 6 , respecting, that + binds left-associative
(4 + 3) * (5 - 2)
4 + (4 / (2 - 2))
到目前为止,此逻辑工作正常,即:
eval :: Exp -> Int
eval (Num a) = a
eval (a :+: b) = (eval a) + (eval b)
eval (a :-: b) = (eval a) - (eval b)
eval (a :*: b) = (eval a) * (eval b)
eval (a :/: b) = (eval a) `div` (eval b)
当我通过此操作时,我得到3
作为正确的输出:
eval (Num 2 :+: Num 2 :-: Num 1)
output = 3
但是我很困惑它如何评估表达式的第二部分,:-:
我理解在第一次迭代中唯一匹配的模式是eval (a :+: b) = (eval a) + (eval b)
,它返回了4
作为输出,它如何进行4
到下一次迭代以执行:-:
操作?
在我尝试这样的操作之前:
eval :: Exp -> Int
eval (Num a) = a
eval (Num a :+: Num b) = eval (Num a) + eval (Num b)
eval (Num a :-: Num b) = eval (Num a) - eval (Num b)
eval (Num a :*: Num b) = eval (Num a) * eval (Num b)
eval (Num a :/: Num b) = eval (Num a) `div` eval (Num b)
它没有给出期望的结果,然后我将其更改为第一个版本。它工作正常,但没有得到正确的语义。有什么帮助吗?请不要犹豫,进入细节。提前致谢。
答案 0 :(得分:6)
运算符的默认固定性为infixl 9
(请参见4.4.2 Fixity Declarations
)。
您尚未声明:+:
等运算符的固定性。
这意味着您的表达式被解析为
eval ((Num 2 :+: Num 2) :-: Num 1) :: Exp
-- Num 2 :: Exp
-- Num 2 :: Exp
-- Num 2 :+: Num 2 :: Exp
-- Num 1 :: Exp
-- (Num 2 :+: Num 2) :-: Num 1 :: Exp
所以评估按
进行 eval ((Num 2 :+: Num 2) :-: Num 1) -- match with:
eval (a :-: b ) = (eval a) - (eval b)
= ....
答案 1 :(得分:5)
正如其他答案所指出的那样,问题在于所有构造函数都具有相同的默认优先级:它们是具有高(9)优先级的左关联。您可以使用infixl
指令来更改它。
data Exp = Num Int
| Exp :+: Exp
| Exp :-: Exp
| Exp :*: Exp
| Exp :/: Exp
deriving (Show)
-- Using the same precedences as (+), (-), (*), and (/)
infixl 6 :+:, :-:
infixl 7 :*:, :/:
现在eval $ Num 4 :+: Num 2 :*: Num 3
将正确返回10而不是18。
答案 2 :(得分:1)
我认为,跟踪此处发生的情况的最简单方法是每次仅一步步跟踪评估。正如@WillNess解释的那样,默认的操作顺序是从左到右,因此您的代码将按以下方式进行评估(每个步骤都直接来自eval
的定义i的一行):
eval ((Num 2 :+: Num 2) :-: Num 1)
eval (Num 2 :+: Num 2) - eval(Num 1)
(eval (Num 2 :+: Num 2)) - 1
(eval (Num 2) + eval (Num 2)) - 1
(2 + 2) - 1
4 - 1
3
我希望这可以弄清楚。