Secondo esempio, i combinatori.

Per finire i commenti sul secondo esempio esaminiamo le differenze tra l’utilizzo della composizione di funzioni e dell’applicazione a bassa precedenza , rispetto all’uso di parentesi.
Per esempio, ammettiamo di voler contare gli spicchi di un’arancia. Per farlo dobbiamo pelarla e dividerla. Quindi per descrivere l’operazione a chi deve eseguirla scriveremmo su un foglio “pela, dividi e conta”. Se volessimo scrivere il programma useremmo le parentesi per dare la precedenza alle operazioni.

contaspicchi x = conta (dividi (pela x))

Notiamo tuttavia che pela, dividi e conta hanno la stessa struttura, ricevono una cosa in ingresso e ne fanno uscire una nuova. Proviamo a scrivere le firme.

pela :: Arancia -> Pelata
dividi :: Pelata -> Spicchi
conta :: Spicchi -> Int

La loro combinazione è contaspicchi

contaspicchi :: Arancia -> Int

Allora ci inventiamo una funzione per combinare questo tipo di operazioni. Chiamiamola e.
La definizione di contaspicchi sarebbe:

contaspicchi = e conta  (e dividi pela)

Ecco la definizione di e

e f g x = f (g x)

Così non sarebbe un gran che, ma haskell ci permette di usare dei simboli al posto delle funzioni e la funzione e è già nella libreria di base sotto forma di operatore .

Ma prima di tutto vediamo la sottile differenza tra operatore e funzione.
Gli operatori sono funzioni di esattamente due argomenti.
Il loro nome contiene solo simboli che non possono essere utilizzati nei nomi di funzioni generiche.
La loro posizione è sempre in mezzo ai loro due argomenti.
Vediamone un paio famosi.

Prelude> :t (+)
(+) :: Num a => a -> a -> a
Prelude> :t (-)
(-) :: Num a => a -> a -> a

Le parentesi intorno ad un operatore lo trasformano in funzione.

Prelude> 3 + 2
5
Prelude> (+) 3 2
5

Quindi quando osserviamo la firma di un operatore la troveremo con l’operatore usato come funzione, ovvero con le parentesi intorno.
L’operatore . che si chiama combinazione di funzione è funzione di due argomenti.

Prelude> :t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c

Sgrunt, ma qui gli argomenti sono 3.. allora lo riscriviamo così:

(.) :: (b -> c) -> (a -> b) -> (a -> c)

In seguito si capirà che questa operazione è corretta.
Il primo argomento è una funzione che dal tipo ‘b’ va al tipo ‘c’, scegliamo isUpper che valuta se un carattere è maiuscolo o meno.

Prelude Data.Char> :t isUpper 
isUpper :: Char -> Bool

Quindi in questo caso ‘b’ è Char e ‘c’ è Bool.
Dalla firma di (.) scegliamo una funzione da ‘a’ a ‘b’, con ‘b’ fissato a Char dalla prima. La funzione è chr

Prelude Data.Char> :t chr
chr :: Int -> Char

Questa fissa ‘a’ ad essere Int.

Riscrivendo la firma del combinatore con i tipi scelti abbiamo:

(.) :: (Char -> Bool) -> (Int -> Char) -> (Int -> Bool)

Vediamo cosa dice l’interprete.

Prelude Data.Char> :t isUpper . chr
isUpper . chr :: Int -> Bool

Possiamo usare questa funzione per vedere i numeri delle lettere maiuscole

Prelude Data.Char> [(x,chr x, (isUpper . chr) x) | x <- [60 .. 80]]
[(60,'',False),(63,'?',False),(64,'@',False),(65,'A',True),(66,'B',True),(67,'C',True),(68,'D',True),(69,'E',True),(70,'F',True),(71,'G',True),(72,'H',True),(73,'I',True),(74,'J',True),(75,'K',True),(76,'L',True),(77,'M',True),(78,'N',True),(79,'O',True),(80,'P',True)]

L’ultimo sforzo serve ad eliminare le parentesi in (isUpper. chr) x che ora sappiamo coincidere con isUpper (chr x).
Per farlo introduciamo l’applicazione di funzione esplicita di ($).
Quando scriviamo f x intendiamo la funzione f applicata ad x, quindi se f :: a -> b allora x :: a e f x :: b.
L’operatore ($) sostituisce l’applicazione e quindi f $ x = f x ma, mentre l’applicazione scritta con lo spazio ha precedenza altissima, quella scritta con ($) ha precedenza bassissima e comunque più bassa di (.) .
Risultato (isUpper . chr) x si può scrivere isUpper . chr $ x ed è così che lo troveremo scritto spesso nel codice haskell.

Tornando infine al nostro esempio

Rotate (2*y) . Translate 0 y $ Circle y

riconosciamo il pattern f . g $ x che quindi sappiamo riscrivere f (g x) ovvero la forma naturale con parentesi Rotate (2*y) (Translate 0 y (Circle y))

Annunci

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione / Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione / Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione / Modifica )

Google+ photo

Stai commentando usando il tuo account Google+. Chiudi sessione / Modifica )

Connessione a %s...