2014-12-16 16 views
5

apprendimento Haskell e io non sono sicuro perché non ottengo il risultato atteso, dato queste definizioni:Non capisco perché questo modello corrisponde guardia

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 

Ho scritto questa funzione

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 

Tuttavia :

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

Se scrivo una versione specifica per 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 

Quindi ho ottenuto il risultato previsto.

Perché (Lit mulId) <- x corrisponde anche quando x non lo è (Lit 1)?

+5

'mulId' è una nuova variabile locale, non correlata a quella precedentemente definita. Vuoi 'Lit w <- x, w == mulId = ...' invece. – chi

risposta

9

Le variabili utilizzate nella corrispondenza del modello sono considerate variabili locali. Prendere in considerazione questa definizione per il calcolo della lunghezza di una lista:

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

Variabili x e xs sono variabili locali a questa definizione. In particolare, se si aggiunge una definizione per una variabile di alto livello, come in

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

questo non non influenzano il significato per len. Più in dettaglio, il primo modello (x:xs) è non equivalente a (10:xs). Se fosse interpretato in questo modo, avremmo ora len [5,6] == 0, rompendo il codice precedente! Fortunatamente, la semantica della corrispondenza dei modelli è solida rispetto alle nuove dichiarazioni come x=10.

Il codice

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 

significa in realtà

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 

che è sbagliato, dal momento che w può essere arbitrario. Ciò che si vuole è probabilmente:

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 

(Il vincolo Eq a può dipendere la definizione di RingExpr, che non è stato pubblicato)

Si può anche semplificare il tutto a:

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 

o addirittura a:

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 

Questa versione non ha nemmeno usa le guardie del modello, poiché non ce n'è bisogno.

+0

Grazie! Nell'ultima nota, "puoi anche semplificare" - su quale base è più semplice? solo nel senso che non usa guardie del modello? o, forse è più idiomatico evitare le guardie del modello se possibile? –

+0

@ j-a Le protezioni di pattern sono un'estensione GHC di Haskell, che è utile quando è necessario scrivere per es. 'f x y | Solo z <- g (x + y) = ... 'dove c'è un'espressione complessa a destra di' <-'. Quando si usa invece 'pattern <- x', il costrutto' x @ pattern' standard è sufficiente. Quest'ultimo è anche più idiomatico. Qual è in realtà più semplice è ovviamente una questione di gusti. – chi

+0

thx, c'è qualche ragione particolare per cui hai usato x @ .. anche se non richiesto? squashMul x @ (Acceso w) y | w == mulId = y => squashMul (Lit w) y | w == mulId = y –

Problemi correlati