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
wrappingevalM1
。代码式
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
withevalM=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) = ...