表达式单子
Expression Monad
我接到任务:
data Exp = Con Int | Sum Exp Exp | Division Exp Exp
编写函数evalM,计算表达式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
这里有一个提示。
首先,您必须了解为什么评估结果是 Maybe Int
而不是更简单的 Int
。直观地说,Maybe Int
包含 Int
的所有可能值,外加一个特殊值 (Nothing
)。附加价值是什么?
很可能,该附加值将用于捕获被零除的错误。也就是说,如果我们需要在表达式中除以零,我们应该 return Nothing
而不是导致运行时错误。
您的 evalM1
函数忽略了这个事实,只是尝试除以数字,returning 一个简单的 Int
。不过,这个 Int
return 类型是半谎言,因为除以零并没有真正处理。除以零会导致运行时错误,而不是更好的特殊值 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
块内执行这两个调用,获取它们的结果(如果有的话,回想一下它们可以 return Nothing
),然后最后是 +
和 return
总和。
除法类似,但记得检查0
。
最后,让我谈谈你的 "question as asked"。为什么 x <- Just number
只适用于 Just
。好吧,那是因为我们在 Maybe
monad 内部工作,所以 <-
之后的每个表达式都必须具有 Maybe something
形式的类型。所以我们不能写x <- (42 :: Int)
。如果没有 Just
,我们可以在 do
块中写入 let x = (42 :: Int)
,但您不需要那个。
我接到任务:
data Exp = Con Int | Sum Exp Exp | Division Exp Exp
编写函数evalM,计算表达式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
这里有一个提示。
首先,您必须了解为什么评估结果是 Maybe Int
而不是更简单的 Int
。直观地说,Maybe Int
包含 Int
的所有可能值,外加一个特殊值 (Nothing
)。附加价值是什么?
很可能,该附加值将用于捕获被零除的错误。也就是说,如果我们需要在表达式中除以零,我们应该 return Nothing
而不是导致运行时错误。
您的 evalM1
函数忽略了这个事实,只是尝试除以数字,returning 一个简单的 Int
。不过,这个 Int
return 类型是半谎言,因为除以零并没有真正处理。除以零会导致运行时错误,而不是更好的特殊值 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
块内执行这两个调用,获取它们的结果(如果有的话,回想一下它们可以 return Nothing
),然后最后是 +
和 return
总和。
除法类似,但记得检查0
。
最后,让我谈谈你的 "question as asked"。为什么 x <- Just number
只适用于 Just
。好吧,那是因为我们在 Maybe
monad 内部工作,所以 <-
之后的每个表达式都必须具有 Maybe something
形式的类型。所以我们不能写x <- (42 :: Int)
。如果没有 Just
,我们可以在 do
块中写入 let x = (42 :: Int)
,但您不需要那个。