Haskell运算符的交换属性?

时间:2016-05-19 18:58:16

标签: haskell pattern-matching operators commutativity

有没有办法说明运算符是可交换的,所以我不必为两个方向给出相同的定义?例如:

data Nat = Zero | Succ Nat

(+) :: Nat -> Nat -> Nat
Zero + x = x
x + Zero = x
...

在这里,有没有一种方法可以让我不必同时给出这两种定义,其中一种定义会从另一种定义中暗示出来?有没有办法说明fn = flip fn

2 个答案:

答案 0 :(得分:9)

这个加法运算符没有必要,但一般来说,你可以通过添加翻转参数的最终方程式来实现一个可交换的函数而不实现所有翻转的情况:

data X = A | B | C

adjacent A B = True
adjacent B C = True
adjacent A C = False
adjacent x y = adjacent y x  -- covers B A, C B, and C A

然而,缺点是,如果你忘记处理一个案例,这很容易导致无限循环:

adjacent A B = True
adjacent B C = True
adjacent x y = adjacent y x

此处,adjacent A C会调用adjacent C A,这会调用adjacent A C,依此类推。 GHC的模式匹配穷举检查(-fwarn-incomplete-patterns-Wall)对您没有帮助。

我猜你可以添加一个额外的参数来阻止循环:

data Commute = Forward | Reverse

adjacent = go Forward
  where
    go _ A B = True
    go _ B C = True
    go Forward x y = go Reverse y x  -- try to commute
    go Reverse _ _ = False           -- commuting failed

如果你没有添加go Reverse等式来处理你减刑的情况但是仍然没有匹配,那么GHC会抱怨。

但我认为这只适用于具有大量案例的函数 - 否则,简单地枚举它们就更清楚了。

答案 1 :(得分:2)

把它作为答案:是的,如果你实施定期添加,你会自动结束交换操作:

(+) :: UInt -> UInt -> UInt
Zero + x = x
(Succ s) + x = s + (Succ x)

此操作是可交换的,尽管两种方式都不是有效的,这意味着"big number as UInt" + Zero需要的时间比Zero + "big number as UInt"长,因为加法运算符是以这种方式定义的。

ghci> :set +s
ghci> bignum + Zero
number as uint
(0.01 secs, 4,729,664 bytes) -- inefficient O(n) operation
ghci> Zero + bignum
number as uint
(0.00 secs, 0 bytes) -- instant constant operation

解决这个问题的一个简单方法是定义添加方式,明确定义交换性。