5

学习 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) 也匹配?

4

1 回答 1

9

模式匹配中使用的变量被认为是局部变量。考虑这个用于计算列表长度的定义:

len (x:xs) = 1 + len xs
len _      = 0

变量xxs是此定义的局部变量。特别是,如果我们为顶级变量添加定义,如

x = 10
len (x:xs) = 1 + len xs
len _      = 0

这不影响的含义len更详细地说,第一个模式(x:xs)不等同于. 如果它以这种方式解释,我们现在将有,打破以前的代码!幸运的是,模式匹配的语义对于诸如.(10:xs)len [5,6] == 0x=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

这个版本甚至不使用模式保护,因为没有必要。

于 2014-12-16T18:43:24.367 回答