Коммутативное свойство для операторов Haskell?
есть ли способ заявить, что оператор коммутативен, так что мне не нужно давать одинаковые определения для обоих направлений? Например:
data Nat = Zero | Succ Nat
(+) :: Nat -> Nat -> Nat
Zero + x = x
x + Zero = x
...
вот, есть ли способ, чтобы мне не пришлось давать оба этих определения, что одно из них будет подразумеваться от другого? Есть ли способ, что fn = flip fn
?
2 ответов
это не обязательно для этого оператора сложения, но в целом вы можете сделать функцию коммутативной без реализации всех перевернутых случаев, добавив окончательное уравнение, которое переворачивает аргументы:
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
теперь GHC будет жаловаться, если вы не добавите go Reverse
уравнение для обработки случая, когда вы коммутировали, но все еще не было совпадения.
но я думаю, что это подходит только для функций с большим количеством случаев-в противном случае гораздо яснее просто перечислить их все.
чтобы поставить его в качестве ответа: Да, если вы реализуете регулярное сложение, вы автоматически получаете коммутативную операцию:
(+) :: 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
простой способ исправить это-определить сложение так, как вы это сделали, явно определяя коммутативность.