我编写了一种独特的数据类型来表示基本数学(加法,乘法等),并且它可以工作-但是,当我尝试将其转换为Maybe语句时,没有一个数学起作用。我相信这是一个语法错误,但我尝试使用额外的括号等等,因此无法解决。通常,也许声明很简单,但我不明白为什么它总是引发问题。
这是我创建的数据类型(带有示例):
data Math = Val Int
| Add Math Math
| Sub Math Math
| Mult Math Math
| Div Math Math
deriving Show
ex1 :: Math
ex1 = Add1 (Val1 2) (Val1 3)
ex2 :: Math
ex2 = Mult (Val 2) (Val 3)
ex3 :: Math
ex3 = Div (Val 3) (Val 0)
这是代码。唯一的零回报应该是除以零。
expression :: Math -> Maybe Int
expression (Val n) = Just n
expression (Add e1 e2) = Just (expression e1) + (expression e2)
expression (Sub e1 e2) = Just (expression e1) - (expression e2)
expression (Mult e1 e2) = Just (expression e1) * (expression e2)
expression (Div e1 e2)
| e2 /= 0 = Just (expression e1) `div` (expression e2)
| otherwise = Nothing
即使删除其他数学方程式,对于每个单独的数学方程式,我也会遇到相同的错误,因此可以肯定这是语法。该错误使它看起来像是Maybe中的Maybe,但是当我执行e1 /= 0 && e2 /= 0 = Just (Just (expression e1)
div (expression e2))
时,我得到了相同的错误:
* Couldn't match type `Int' with `Maybe Int'
Expected type: Maybe (Maybe Int)
Actual type: Maybe Int
* In the second argument of `div', namely `(expression e2)'
In the expression: Just (expression e1) `div` (expression e2)
In an equation for `expression':
expression (Div e1 e2)
| e1 /= 0 && e2 /= 0 = Just (expression e1) `div` (expression e2)
| otherwise = Nothing
|
56 | | e1 /= 0 && e2 /= 0 = Just (expression e1) `div` (expression e2)
| ^^^^^^^^^
我想念什么?这让我发疯。
答案 0 :(得分:7)
第一个问题是优先级。不用写:
Just (expression e1) * (expression e2)
您可能想要:
Just (expression e1 * expression e2)
第二个问题是类型。看一下(*)的类型,例如:
>>> :t (*)
(*) :: Num a => a -> a -> a
对于一个a
类型的Num
,它需要两个a
,并返回一个a
。专用于Int
,那就是:
(*) :: Int -> Int -> Int
但是expression
返回一个Maybe Int
!因此,我们需要某种与Maybe
相乘的方法。让我们自己编写函数:
multMaybes :: Maybe Int -> Maybe Int -> Maybe Int
multMaybes Nothing _ = Nothing
multMaybes _ Nothing = Nothing
multMaybes (Just x) (Just y) = Just (x * y)
因此,如果乘法的任何一方都失败了(即您找到了被零除),那么整个事情就会失败。现在,我们需要为每个操作员执行一次操作:
addMaybes Nothing _ = Nothing
addMaybes _ Nothing = Nothing
addMaybes (Just x) (Just y) = Just (x + y)
subMaybes Nothing _ = Nothing
subMaybes _ Nothing = Nothing
subMaybes (Just x) (Just y) = Just (x - y)
以此类推。但是我们可以看到这里有很多重复。幸运的是,已经有一个函数可以执行此模式:liftA2
。
multMaybes = liftA2 (*)
addMaybes = liftA2 (+)
subMaybes = liftA2 (-)
最后,还有两个小问题。首先,你说:
expression (Div e1 e2)
| e2 /= 0 = Just (expression e1) `div` (expression e2)
但是e2
不是Int
!这是表达式类型。您可能要检查递归调用的结果是否为0。
第二个问题是您不必要在Just
中包裹东西:我们可以删除一层。
所有这些之后,我们可以像这样编写您的函数:
expression :: Math -> Maybe Int
expression (Val n) = Just n
expression (Add e1 e2) = liftA2 (+) (expression e1) (expression e2)
expression (Sub e1 e2) = liftA2 (-) (expression e1) (expression e2)
expression (Mult e1 e2) = liftA2 (*) (expression e1) (expression e2)
expression (Div e1 e2)
| r2 /= Just 0 = liftA2 div (expression e1) r2
| otherwise = Nothing
where r2 = expression e2
答案 1 :(得分:4)
这里有两个问题:
Just (expression e1) + (expression e2)
被解释为:
(Just (expression e1)) + (expression e2)
因此,这意味着您已将左值包装在Just
中,而另一个未包装,这没有多大意义。
第二,expression e1
和expression e2
的类型均为Maybe Int
,因此这意味着您无法将这两个变量加在一起。我们可以执行模式匹配。
幸运的是,有一个更优雅的解决方案:对于大多数 模式,我们可以使用liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c
。对于Maybe
,liftM2
将使用一个函数f :: a -> b -> c
和两个Maybe
,如果两者都是Just
,它将在以下值上调用该函数:包裹在Just
中,然后将结果也包裹在Just
中。
对于除法情况,我们首先必须使用expression
函数获得分母的结果,如果它是{em> not 等于的Just
设为零,那么我们可以使用fmap :: Functor f => (a -> b) -> f a -> f b
函数将值映射到Just
(分子的值)中,当然,分子是Just
:
import Control.Monad(liftM2)
expression :: Math -> Maybe Int
expression (Val n) = Just n
expression (Add e1 e2) = liftM2 (+) (expression e1) (expression e2)
expression (Sub e1 e2) = liftM2 (-) (expression e1) (expression e2)
expression (Mult e1 e2) = liftM2 (*) (expression e1) (expression e2)
expression (Div e1 e2) | Just v2 <- expression e2, v2 /= 0 = fmap (`div` v2) (expression e1)
| otherwise = Nothing
或者像@RobinZigmond所说的那样,我们可以使用(<$>) :: Functor f => (a -> b) -> f a -> f b
和(<*>) :: Applicative f => f (a -> b) -> f a -> f b
:
expression :: Math -> Maybe Int
expression (Val n) = Just n
expression (Add e1 e2) = (+) <$> expression e1 <*> expression e2
expression (Sub e1 e2) = (-) <$> expression e1 <*> expression e2
expression (Mult e1 e2) = (*) <$> expression e1 <*> expression e2
expression (Div e1 e2) | Just v2 <- expression e2, v2 /= 0 = (`div` v2) <$> expression e1
| otherwise = Nothing