Не уверен, почему этот образец охранника соответствует
Изучаю 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
совпадать, даже если х не (горит 1)?
1 ответ
Переменные, используемые при сопоставлении с образцом, считаются локальными переменными. Рассмотрим это определение для вычисления длины списка:
len (x:xs) = 1 + len xs
len _ = 0
переменные x
а также xs
являются локальными переменными для этого определения. В частности, если мы добавим определение для переменной верхнего уровня, как в
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
(The 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
Эта версия даже не использует паттерны, так как в этом нет необходимости.