2014-12-16 19 views
5

Learning Haskell i nie jestem pewien, dlaczego nie uzyskać oczekiwany rezultat, biorąc pod uwagę te definicje:Nie wiem, dlaczego ten strażnik wzorzec pasuje

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 

Napisałem tę funkcję

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 

jednak :

*HW05> squashMul (Lit 5) (Lit 1) 
Lit 1 

Jeśli piszę jedna wersja specjalnie dla 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 

Otrzymuję oczekiwany wynik.

Dlaczego (Lit mulId) <- x pasuje nawet, gdy x nie jest (Lit 1)?

+5

'mulId' jest nową zmienną lokalną, niezwiązaną z poprzednio zdefiniowaną. Zamiast tego chcesz "Lit w <- x, w == mulId = ...". – chi

Odpowiedz

9

Zmienne używane do porównywania wzorców są uważane za zmienne lokalne. Rozważmy tę definicję do obliczania długości listy:

len (x:xs) = 1 + len xs 
len _  = 0 

zmiennych x i xs są zmienne lokalne do tej definicji. W szczególności, jeśli dodamy do definicji zmiennej na najwyższym poziomie, jak w

x = 10 
len (x:xs) = 1 + len xs 
len _  = 0 

to robi nie wpływa na znaczenie dla len. Bardziej szczegółowo, pierwszy wzorzec (x:xs) jest równy , a nie, co odpowiada . Jeśli zostanie to zinterpretowane w ten sposób, będziemy mieli teraz len [5,6] == 0, łamiąc poprzedni kod! Na szczęście semantyka dopasowywania wzorców jest odporna na takie nowe deklaracje, jak x=10.

Kod

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 

faktycznie oznacza

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 

co jest błędne, ponieważ w może być dowolna. Co chcesz, to prawdopodobnie:

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 ograniczenie może zależeć od definicji RingExpr, która nie została wysłana)

Można również uprościć wszystko:

squashMul :: (Eq a, Ring a) => RingExpr a -> RingExpr a -> RingExpr a 
squashMul [email protected](Lit w) y   | w == mulId = y 
squashMul x   [email protected](Lit w) | w == mulId = x 
squashMul x   y      = Mul x y 

lub nawet na:

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 

Ta wersja nie ma nawet używaj szablonów, ponieważ nie ma takiej potrzeby.

+0

Dzięki! Na ostatniej nucie "możesz także uprościć" - na jakiej podstawie jest to prostsze? tylko dlatego, że nie korzysta ze strażników wzorujących? lub, być może, bardziej idiomatyczne jest unikanie, jeśli to możliwe, wzorców? –

+0

@ j-a Ochrona przed wzorami to rozszerzenie GHC Haskella, które jest przydatne, gdy trzeba napisać np. 'f x y | Po prostu z <- g (x + y) = ... 'gdzie jest złożone wyrażenie po prawej stronie' <-'. W przypadku użycia zamiast 'wzorca <- x' wystarcza standardowa konstrukcja' x @ wzór '. Ten drugi jest również bardziej idiomatyczny. Który z nich jest rzeczywiście prostszy, jest oczywiście kwestią gustu. – chi

+0

thx, czy istnieje jakiś szczególny powód, dla którego użyłeś x @ .. nawet jeśli nie jest to wymagane? squashMul x @ (Lit w) y | w == mulId = y => squashMul (Lit w) y | w == mulId = y –

Powiązane problemy