鉴于以下定义,学习 Haskell 并且我不确定为什么我没有得到预期的结果:
instance Ring Integer where
addId = 0
addInv = negate
mulId = 1
add = (+)
mul = (*)
class Ring a where
addId :: a -- additive identity
addInv :: a -> a -- additive inverse
mulId :: a -- multiplicative identity
add :: a -> a -> a -- addition
mul :: a -> a -> a -- multiplication
我写了这个函数
squashMul :: (Ring a) => RingExpr a -> RingExpr a -> RingExpr a
squashMul x y
| (Lit mulId) <- x = y
| (Lit mulId) <- y = x
squashMul x y = Mul x y
然而:
*HW05> squashMul (Lit 5) (Lit 1)
Lit 1
如果我专门为 Integer 编写一个版本:
squashMulInt :: RingExpr Integer -> RingExpr Integer -> RingExpr Integer
squashMulInt x y
| (Lit 1) <- x = y
| (Lit 1) <- y = x
squashMulInt x y = Mul x y
然后我得到了预期的结果。
为什么
(Lit mulId) <- x
即使 x 不是 (Lit 1) 也匹配?
最佳答案
模式匹配中使用的变量被认为是局部变量。考虑这个计算列表长度的定义:
len (x:xs) = 1 + len xs
len _ = 0
变量
x
和 xs
是此定义的局部变量。特别是,如果我们为顶级变量添加定义,如x = 10
len (x:xs) = 1 + len xs
len _ = 0
这不影响
len
的含义.更详细的,第一个图案(x:xs)
不等于 (10:xs)
.如果这样解释,我们现在会有 len [5,6] == 0
,破解之前的密码!幸运的是,模式匹配的语义对于诸如 x=10
这样的新声明是健壮的。 .你的代码
squashMul :: (Ring a) => RingExpr a -> RingExpr a -> RingExpr a
squashMul x y
| (Lit mulId) <- x = y
| (Lit mulId) <- y = x
squashMul x y = Mul x y
实际上是指
squashMul :: (Ring a) => RingExpr a -> RingExpr a -> RingExpr a
squashMul x y
| (Lit w) <- x = y
| (Lit w) <- y = x
squashMul x y = Mul x y
这是错误的,因为
w
可以随意。你想要的大概是:squashMul :: (Eq a, Ring a) => RingExpr a -> RingExpr a -> RingExpr a
squashMul x y
| (Lit w) <- x , w == mulId = y
| (Lit w) <- y , w == mulId = x
squashMul x y = Mul x y
(
Eq a
约束可能取决于未发布的 RingExpr
的定义)您还可以将所有内容简化为:
squashMul :: (Eq a, Ring a) => RingExpr a -> RingExpr a -> RingExpr a
squashMul x@(Lit w) y | w == mulId = y
squashMul x y@(Lit w) | w == mulId = x
squashMul x y = Mul x y
甚至:
squashMul :: (Eq a, Ring a) => RingExpr a -> RingExpr a -> RingExpr a
squashMul (Lit w) y | w == mulId = y
squashMul x (Lit w) | w == mulId = x
squashMul x y = Mul x y
这个版本甚至不使用模式保护,因为没有必要。
关于haskell - 不知道为什么这个模式守卫匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27511159/