2014-12-16 12 views
5

Lernen Haskell und ich bin nicht sicher, warum ich diese Definitionen gegeben nicht das erwartete Ergebnis:nicht sicher, warum dieses Muster guard Spiele

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 

ich diese Funktion

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 

schrieb jedoch :

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

Wenn ich eine Version speziell für Integer schreiben:

squashMulInt :: RingExpr Integer -> RingExpr Integer -> RingExpr Integer 
squashMulInt x y 
    | (Lit 1) <- x = y 
    | (Lit 1) <- y = x 
squashMulInt x y = Mul x y 

Dann bekomme ich das erwartete Ergebnis.

Warum stimmt (Lit mulId) <- x überein, obwohl x nicht (Lit 1) ist?

+5

'mulId' ist eine neue lokale Variable, die nicht mit der zuvor definierten übereinstimmt. Sie möchten stattdessen 'Lit w <- x, w == mulId = ...'. – chi

Antwort

9

Bei der Mustererkennung verwendete Variablen werden als lokale Variablen betrachtet. Betrachten Sie diese Definition für die Berechnung der Länge einer Liste:

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

Variablen x und xs sind lokale Variablen dieser Definition. Insbesondere dann, wenn wir eine Definition für einen Top-Level-Variable, wie in

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

dies tut nicht Einfluss auf die Bedeutung für len hinzufügen. Genauer gesagt ist das erste Muster (x:xs)nicht äquivalent zu (10:xs). Wenn es auf diese Weise interpretiert würde, hätten wir jetzt len [5,6] == 0, wodurch der vorherige Code gebrochen wird! Glücklicherweise ist die Semantik des Mustervergleichs robust gegenüber solchen neuen Deklarationen wie x=10.

Ihr Code

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 

bedeutet eigentlich

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 

was falsch ist, da w kann beliebig sein. Was Sie wollen, ist wahrscheinlich:

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 

(Die Eq a Einschränkung für die Definition von RingExpr abhängen kann, die nicht veröffentlicht wurde)

Sie auch alles vereinfachen:

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 

oder sogar zu:

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 

Diese Version ist nicht einmal Verwende Musterwächter, da es nicht nötig ist.

+0

Danke! Auf der letzten Note, "Sie können auch vereinfachen" - auf welcher Grundlage ist es einfacher? nur weil es keine Musterwächter verwendet? oder ist es vielleicht idiomatischer, Musterwächter zu vermeiden, wenn möglich? –

+0

@ j-a Musterwächter sind eine GHC-Erweiterung von Haskell, die nützlich ist, wenn Sie z. 'f x y | Just z <- g (x + y) = ... 'wo es einen komplexen Ausdruck rechts von' <-' gibt. Wenn stattdessen "Muster <- x" verwendet wird, ist das Standardmuster "x @ Muster" ausreichend. Letzteres ist auch idiomatischer. Was eigentlich einfacher ist, ist natürlich Geschmackssache. – chi

+0

thx, gibt es einen bestimmten Grund, warum Sie x @ .. verwendet haben, obwohl nicht erforderlich? squashMul x @ (Lit w) y | w == mulId = y => squashMul (Lit w) y | w == mulId = y –