我得到了任务:
data Exp = Con Int | Sum Exp Exp | Division Exp Exp
编写函数evalM,该函数评估Expression Exp并将其放在Maybe中。 不允许使用Just构造函数,而必须使用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
所以我的问题是,为什么第一个evalM在没有Just的情况下起作用,而第二个evalM仅在我使用Just(y <-Just ..)的情况下起作用?
也许有人可以用>> =给我写解决方案。
非常感谢您!
解决方案:
evalM :: Exp -> Maybe Int
evalM (Con i) = return i
evalM (Neg x) = evalM x >>= \v -> return (-v)
evalM (Sum e1 e2) = evalM e1 >>= \a -> evalM e2 >>= \b -> (return (a+b))
evalM (Division e1 e2) = evalM e1 >>= \a -> evalM e2 >>= (\b -> guard(b/=0) >> (return (div a b)))
> *Test> evalM (Division (Con 1) (Division (Con 0) (Con 1))) ~> Nothing
> *Test> evalM (Division (Con 1) (Sum (Con 2) (Neg (Con 2)))) ~> Nothing
答案 0 :(得分:1)
这是一个提示。
首先,您必须了解为什么评估结果是Maybe Int
而不是简单的Int
。直观地讲,Maybe Int
包含Int
的所有可能值,以及一个特殊的值(Nothing
)。额外的价值是什么?
同样,该附加值将用于捕获零误差除法。也就是说,如果需要在表达式中除以零,则应返回Nothing
而不是导致运行时错误。
您的evalM1
函数将忽略此事实,仅尝试对数字进行除法,返回简单的Int
。不过,此Int
返回类型是一个半谎言,因为实际上并没有处理被零除的问题。除以零会导致运行时错误,而不是更好的特殊值Nothing
。
请注意,我们不能简单地定义evalM :: Exp -> Maybe Int
包装evalM1
。像
evalM e = return (evalM1 e)
会输入,但在特殊情况下不会处理零除。
我建议您忽略evalM1
,而从头开始构建evalM
。您可以遵循类似的递归模式:
evalM :: Exp -> Maybe Int
evalM (Con i) = ...
evalM (Sum e1 e2) = ...
evalM (Division e1 e2) = ...
对于总和情况:请注意,我们不能编写递归调用evalM e1 + evalM e2
,因为我们不能+
两个Maybe Int
,因为它们不是数字。但是,这不是一个真正的问题。相反,我们可以在do
块中执行两次调用,以获取它们的结果(如果有,请回想它们可以返回Nothing
),然后最后是+
和return
除法类似,但请记住检查0
。
最后,请允许我解决您的“所问问题”。为什么x <- Just number
仅适用于Just
。好吧,这是因为我们在这里工作于Maybe
monad中,因此<-
之后的每个表达式都必须具有Maybe something
形式的类型。所以我们不能写x <- (42 :: Int)
。如果没有Just
,我们可以在let x = (42 :: Int)
块中编写do
,但是您不需要。