haskell - 不知道为什么这个模式守卫匹配

标签 haskell pattern-guards

鉴于以下定义,学习 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

变量 xxs是此定义的局部变量。特别是,如果我们为顶级变量添加定义,如
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/

相关文章:

haskell - 从参数的约束推断类型族的约束

list - 在新行中打印列表元素

haskell - 在 Haskell 中是否有类似于 sub-guard 的东西?

haskell - 为什么 FingerTrees 的使用不足以实现稳定的实现?

list - Haskell 展平字符串列表

list - 使用 Map 应用具有多个输入的函数? ( haskell )

haskell - 如何在状态 monad 的字段上进行模式匹配?

Scala 匹配带有守卫的元组列表

haskell - 如何让 Haskell 中的函数取决于其参数的类型?