Haskell 运算符的交换 属性?
Commutative Property for Haskell Operators?
有没有一种方法可以说明运算符是可交换的,这样我就不必为两个方向给出相同的定义?例如:
data Nat = Zero | Succ Nat
(+) :: Nat -> Nat -> Nat
Zero + x = x
x + Zero = x
...
这里,有没有一种方法可以让我不必给出这两个定义,其中一个可以从另一个中隐含?有什么方法可以说明 fn = flip fn
?
作为答案:是的,如果您实施常规加法,您会自动以交换操作结束:
(+) :: 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
解决此问题的一个简单方法是按照您的方式定义加法,显式定义交换性。
这个加法运算符不是必需的,但通常您可以通过添加翻转参数的最终方程来使函数可交换,而无需实现所有翻转的情况:
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
但是,缺点是如果忘记处理case,很容易导致死循环:
adjacent A B = True
adjacent B C = True
adjacent x y = adjacent y x
这里,adjacent A C
会调用adjacent C A
,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 会抱怨。
不过我觉得这只适用于case比较多的函数,不然直接枚举一下就清楚多了
有没有一种方法可以说明运算符是可交换的,这样我就不必为两个方向给出相同的定义?例如:
data Nat = Zero | Succ Nat
(+) :: Nat -> Nat -> Nat
Zero + x = x
x + Zero = x
...
这里,有没有一种方法可以让我不必给出这两个定义,其中一个可以从另一个中隐含?有什么方法可以说明 fn = flip fn
?
作为答案:是的,如果您实施常规加法,您会自动以交换操作结束:
(+) :: 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
解决此问题的一个简单方法是按照您的方式定义加法,显式定义交换性。
这个加法运算符不是必需的,但通常您可以通过添加翻转参数的最终方程来使函数可交换,而无需实现所有翻转的情况:
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
但是,缺点是如果忘记处理case,很容易导致死循环:
adjacent A B = True
adjacent B C = True
adjacent x y = adjacent y x
这里,adjacent A C
会调用adjacent C A
,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 会抱怨。
不过我觉得这只适用于case比较多的函数,不然直接枚举一下就清楚多了