Funzioni anonime e sezioni
In Haskell le funzioni sono entità di prima classe, nel senso che è possibile usare le funzioni come ogni altro tipo di dato. È possibile scrivere funzioni che accettano funzioni come argomento, così come è possibile scrivere funzioni che restituiscono altre funzioni come risultato. Non solo, per mezzo di cosiddette lambda espressioni è possibile definire funzioni “al volo” laddove necessario, proprio come è possibile inserire una costante numerica o logica all’interno di una espressione qualsiasi.
Funzioni anonime
A titolo di esempio, riconsideriamo la funzione successore
vista
in precedenza:
successore :: Int -> Int
successore x = x + 1
Questa definizione stabilisce che il valore prodotto dalla funzione
successore
quando è applicata a un numero intero x
è pari a x +
1
. Un modo alternativo e del tutto equivalente di definire la
stessa funzione è il seguente:
successore :: Int -> Int
successore = \x -> x + 1
Questa definizione stabilisce che successore
è la funzione che,
applicata a un numero intero x
, produce x + 1
. Si noti come
l’argomento x
della funzione è passato da sinistra a destra del
simbolo =
. L’espressione a destra del simbolo =
è una funzione
anonima anche detta lambda espressione. In particolare
\x -> x + 1
è una espressione Haskell che rappresenta la funzione (senza nome)
che, applicata a un numero x
, produce x + 1
. Il simbolo \
che
precede l’argomento della funzione richiama la forma della lettera
greca lambda $\lambda$, da cui deriva il nome di questa particolare
forma di espressione.
Per convincersi che le funzioni anonime sono espressioni a tutti gli effetti, è possibile fare alcuni semplici esperimenti in cui si applica una funzione anonima subito dopo averla definita, ma senza averle dato un nome.
(\x -> x + 1) 2
(\x -> x >= 0) 2
(\x -> x + 1) (negate 2)
Come al solito, l’applicazione funzionale si indica ponendo funzione
e argomento una di fianco all’altro. In questi esempi, le parentesi
attorno alla funzione anonima sono necessarie in quanto, per
convenzione, il corpo di una funzione anonima si estende il più
possibile a destra della freccia ->
.
Sezioni
In Haskell, si dice sezione un’espressione racchiusa tra parentesi in cui un operatore binario viene applicato a uno solo dei suoi due argomenti. A titolo di esempio, le espressioni
(1 +)
(`mod` 2)
sono sezioni. Nella prima è stato omesso l’operando destro
dell’operatore +
. Nella seconda è stato omesso l’operando sinistro
dell’operatore mod
.
Le sezioni rappresentano funzioni il cui argomento è l’operando mancante. Pertanto, le due sezioni illustrate qui sopra rappresentano la funzione “successore” e la funzione “resto della divisione per 2”, rispettivamente. Siccome non c’è un modo ovvio di “visualizzare una funzione”, la valutazione delle due sezioni proposte qui sopra produce un errore, ma è possibile convincersi della natura delle sezioni applicandole a un argomento:
(1 +) 2
(1 +) 3
(`mod` 2) 2
(`mod` 2) 3
Haskell espande le sezioni in funzioni anonime. Ad esempio, avremo
(1 +) ~~> \x -> 1 + x
(`mod` 2) ~~> \x -> x `mod` 2
dove il simbolo ~~>
è da leggersi “si espande in”. Nota:
l’espansione è fatta automaticamente dal compilatore Haskell ed il
simbolo ~~>
non fa parte della sintassi del linguaggio.
Occorre prestare attenzione al fatto che la funzione risultante
dall’espansione può variare in base alla proprietà di
commutatività dell’operatore. Per esempio, (1 +)
e (+ 1)
sono sezioni diverse che però rappresentano sempre la funzione
“successore”, in quando l’operatore +
è commutativo. Al contrario,
le sezioni (`mod` 2)
e (2 `mod`)
rappresentano funzioni
diverse.
Esercizi
- Scrivere due funzioni anonime che stabiliscano se un numero è
pari o dispari.
\x -> x `mod` 2 == 0 -- essere un numero pari \x -> x `mod` 2 /= 0 -- essere un numero dispari
- Scrivere la funzione anonima “valore assoluto”.
\x -> if x >= 0 then x else negate x
- Ricordando che
.
è l’operatore di composizione funzionale, descrivere a parole le seguenti funzioni. Verificare le proprie risposte valutando in GHCi alcune applicazioni delle funzioni indicate. Suggerimento: può essere d’aiuto espandere le sezioni in lambda espressioni.(< 10)
(10 <)
(`mod` 2)
(1 /)
(+ 1) . (* 2)
(* 2) . (+ 1)
(== 0) . (`mod` 2)
(/= 0) . (`mod` 2)
- La funzione che applicata a un numero $x$ determina se $x$ è minore di 10
- La funzione che applicata a un numero $x$ determina se $x$ è maggiore di 10
- La funzione che applicata a un numero intero $x$ calcola il resto della della divisione di $x$ per 2
- La funzione $\frac{1}{x}$
- La funzione $2x + 1$
- La funzione $2(x + 1)$
- La funzione che applicata a un numero intero $x$ determina se $x$ è pari
- La funzione che applicata a un numero intero $x$ determina se $x$ è dispari