Haskell 表达式单子

Haskell 表达式单子,haskell,expression,monads,maybe,Haskell,Expression,Monads,Maybe,我得到了任务: data Exp = Con Int | Sum Exp Exp | Division Exp Exp 编写函数evalM,它计算表达式Exp并将其放入一个表达式中。 不允许仅使用构造函数,您必须使用DO或>>= 所以我的代码是: evalM1 :: Exp -> Int evalM1 e = evalM2 e where evalM2 :: Exp -> Int evalM2 (Con i) = (i)

我得到了任务:

data Exp = Con Int | Sum Exp Exp | Division Exp Exp
编写函数evalM,它计算表达式Exp并将其放入一个表达式中。 不允许仅使用构造函数,您必须使用DO或>>=

所以我的代码是:

evalM1 :: Exp -> Int
evalM1 e = evalM2 e where
           evalM2 :: Exp -> Int
           evalM2 (Con i) = (i)
           evalM2 (Sum e1 e2) = (evalM2 e1)+(evalM2 e2)
           evalM2 (Division e1 e2) = div (evalM2 e1) (evalM2 e2)

evalM :: Exp -> Maybe Int
evalM e = do
          return (evalM1 e)

-- Second try
evalM e = do
          y <- Just (evalM1 e)
          return y
这里有个提示

首先,您必须理解为什么评估的结果是
可能是Int
,而不是更简单的
Int
。直观地说,
Maybe Int
包含
Int
的所有可能值,加上一个特殊值(
Nothing
)。附加值是多少

很可能,该附加值将用于捕获被零除的错误。也就是说,如果我们需要在表达式中除以零,我们应该返回
Nothing
,而不是导致运行时错误

您的
evalM1
函数忽略这一事实,只是尝试对数字进行除法,返回一个简单的
Int
。但是,这个
Int
返回类型是半个谎言,因为实际上并没有处理被零除的问题。除以零会导致运行时错误,而不是更好的特殊值
Nothing

注意,我们不能简单地定义
evalM::Exp->Maybe Int
wrapping
evalM1
。代码式

evalM e = return (evalM1 e)
将键入,但不会作为特例处理零除

我建议您忽略
evalM1
,而是从头开始构建
evalM
。您可以遵循类似的递归模式:

evalM :: Exp -> Maybe Int
evalM (Con i) = ...
evalM (Sum e1 e2) = ...
evalM (Division e1 e2) = ...
对于求和的情况:请注意,我们不能编写递归调用
evalme1+evalme2
,因为我们不能
+
two
可能Int
,因为它们不是数字。然而,这不是一个真正的问题。相反,我们可以在
do
块中执行这两个调用,获取它们的结果(如果有的话,回忆一下它们可以返回
Nothing
),然后最后返回总和

除法类似,但请记住检查
0


最后,让我谈谈你的“问题”。为什么
x我不理解这个要求
你不能仅仅使用构造函数。如果不使用
Just
,我看不出该怎么做-当然,除非我们被允许使用
return
(或
pure
),这在
的上下文中可能只是同义词…顺便说一句,你的代码让我有点困惑,我不知道为什么您将
coni
评估为
-I
,而不是自然的
I
。如果第二个表达式的计算结果为零,在除法情况下会发生什么?我很确定这就是为什么要求你返回一个
也许Int
,而不仅仅是
Int
@RobinZigmond不完全是同义词
return
pure
是函数,而
Just
是构造函数,这意味着您可以在模式匹配中使用它。我认为约束的想法是禁止您在
Just
上进行模式匹配,同时仍然允许您通过
pure
@amalloy-公平地构建
Just
值,这很有意义:
evalM::Monad m=>Exp->m Int
with
evalM=pure。evalM1将适用于任何monad,
可能包括在内。非常感谢!我添加了我的解决方案,效果非常好。@kingingo-做得好,但我认为您发布的解决方案还不太正确。(虽然它几乎就在那里。)除了当
e2
con0
时(这是您处理的唯一情况),还有一些方法可以让
e1除法e2
给出一个零除法错误。如果您尝试使用
evalM$Division(Con 1)$Sum(Con 2)(Con(-2))会发生什么情况“KEGIGO除以零”并不意味着按字面0除法,考虑例如“代码>1(/ 0/1)< /COD>”,这不是程序所处理的,而是应该处理的。罗宾还建议
1/(2+(-2))
,这也应该得到处理。您应该根据0检查表达式的语义结果,而不是语法。因此,我再次更新了解决方案并添加了一个保护:)。我还测试了它。顺便说一句,非常感谢你们的帮助
evalM :: Exp -> Maybe Int
evalM (Con i) = ...
evalM (Sum e1 e2) = ...
evalM (Division e1 e2) = ...