Haskell中的算术表达式求值

时间:2019-02-14 14:36:58

标签: haskell operators evaluation operator-precedence

在这里,我尝试使用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)

它没有给出期望的结果,然后我将其更改为第一个版本。它工作正常,但没有得到正确的语义。有什么帮助吗?请不要犹豫,进入细节。提前致谢。

3 个答案:

答案 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

我希望这可以弄清楚。