Funzioni
Definizione e applicazione di funzioni
Una funzione rappresenta una particolare trasformazione da un
dominio (l’insieme dei dati che la funzione può accettare) a un
codominio (l’insieme dei dati che la funzione può produrre). Il
seguente esempio definisce la funzione successore, ovvero quella
funzione che, applicata a un numero intero $x$ (di tipo Int
),
produce $x + 1$.
successore :: Int -> Int
successore x = x + 1
Come per le definizioni di valori semplici, anche la definizione di
una funzione è (solitamente) preceduta da una dichiarazione che ne
stabilisce il tipo. Il tipo di una funzione ha la forma $T \to
S$ dove $T$ rappresenta il dominio ed $S$ il codominio. In questo
caso, dominio e codominio sono Int
a indicare il fatto che la
funzione accetta e produce numeri interi (di tipo Int
). La
funzione vera e propria è definita da una equazione successore
x = x + 1
che si può leggere come “la funzione successore
applicata all’argomento x
produce il valore x + 1
”.
Usare una funzione significa applicarla a un argomento. I seguenti
sono esempi di utilizzo della funzione successore
appena definita:
successore 0
successore 1
successore 2 * 3
(successore 2) * 3
successore (2 * 3)
Al solito, l’applicazione di funzione ha priorità rispetto a tutti
gli altri operatori, dunque l’espressione successore 2 * 3
deve
essere interpretata (successore 2) * 3
e non successore (2 * 3)
.
Predicati
Una funzione che ha come codominio il tipo Bool
dei valori di
verità può essere pensata come a un predicato che stabilisce se
il suo argomento soddisfa o meno una certa proprietà. Per esempio,
la seguente funzione determina se un numero intero è pari o no:
pari :: Int -> Bool
pari n = n `mod` 2 == 0
Il valore prodotto dall’applicazione di pari
a n
è il booleano
True
se il resto della divisione di n
per 2 è 0 (dunque se n
è
pari) e False
altrimenti. Ad esempio:
pari 0
pari 2
pari 3
Operatore di composizione funzionale
Possiamo comporre due funzioni usando l’operatore di
composizione funzionale .
(il punto). Per esempio, abbiamo appena
definito al funzione pari
che determina se un numero è pari. Il
predicato “essere un numero dispari” può essere espresso come la
negazione del predicato “essere un numero pari”, nel seguente modo:
dispari :: Int -> Bool
dispari = not . pari
Come in matematica, la funzione composta f . g
trasforma il
proprio argomento da destra verso sinistra. In altre parole, f
. g
è la funzione che, applicata a un argomento x, calcola f (g
x)
. Dunque, not . pari
applicata a un numero n
calcola not
(pari n)
. Notare che l’equazione dispari = not . pari
che
definisce dispari
non dà alcun nome all’argomento della funzione,
ma si limita a dire che dispari
è la funzione ottenuta dalla
composizione di not
e pari
.
Esercizi
- Ridefinire la funzione
dispari
senza fare uso dell’operatore di composizione funzionale.dispari :: Int -> Bool dispari n = n `mod` 2 /= 0
- Un anno è bisestile se è multiplo di 4 ma non di 100, oppure se è
multiplo di 400. Definire un predicato
bisestile :: Int -> Bool
che, applicato a un numero $n$, determini se $n$ è un anno bisestile.bisestile :: Int -> Bool bisestile n = (n `mod` 4 == 0 && n `mod` 100 /= 0) || n `mod` 400 == 0
- È noto che la somma dei primi $n$ numeri naturali è data dalla
formula $\frac{n(n + 1)}2$. Definire una funzione
somma :: Int -> Int
che, applicata a un numero interno $n$ non negativo, calcoli la somma dei primi $n$ numeri naturali usando questa formula.somma :: Int -> Int somma n = (n * (n + 1)) `div` 2
- Definire una funzione
area :: Float -> Float
che, applicata a un numero floating-pointn
non negativo, calcoli l’area del cerchio di raggion
.area :: Float -> Float area n = n ** 2 * pi
- Che funzione è
not . not
?La funzione identità su valori di tipo
Bool
.