Sei sulla pagina 1di 29

Nei linguaggi funzionali puri non esistono strutture di controllo predefinite per la realizzazione di cicli quali for, while,

repeat, ma il principale
meccanismo di controllo la ricorsione.
RICORSIONE
La ricorsione una tecnica per risolvere problemi complessi riducendoli a problemi pi
semplici dello stesso tipo.
Per risolvere un problema ricorsivamente occorre:
1. Identificare i casi semplicissimi (casi di base) che possono essere risolti immediatamente
2. Dato un generico problema complesso, identificare i problemi pi semplici dello stesso
tipo la cui soluzione pu aiutare a risolvere il problema complesso
3. Assumendo di saper risolvere i problemi pi semplici (ipotesi di lavoro), determinare
come operare sulla soluzione dei problemi pi semplici per ottenere la soluzione del
problema complesso

Esempio: la funzione fattoriale


IN IN
n! = 1 2 ... (n 1) n
Per convenzione: 0! = 1
f act(n): metodo ricorsivo per calcolare n!
1. Caso base: f act(0) = 1
2. Volendo calcolare f act(n), per n > 0, calcolare f act(n 1)
3. Per ottenere f act(n), moltiplicare f act(n 1) per n
Definizione ricorsiva del fattoriale:

f act(n) =

1
se n = 0
n f act(n 1) altrimenti

Il fattoriale definito in termini di se stesso, ma per un caso pi facile.

Variabili locali
Nellespressione
let x = E in F
x una variabile locale tale che :
x ha un valore (quello dellespressione E) soltanto allinterno dellespressione F.
quando tutta lespressione let x = E in F stata valutata, x non ha pi un valore.
# let x = 1+2 in x*8;;
- : int = 24
# x;;
Characters 0-1:
x;;
^
Unbound value x
Il legame locale sovrascrive altri eventuali legami globali
# let x="pippo";;
val x : string = "pippo"
# let x="pluto" in "ciao "^x;;
- : string = "ciao pluto"
# x;;
- : string = "pippo"

(* concatenazione di stringhe *)

Valutazione di una dichiarazione locale


Per valutare unespressione della forma
let x = E in F
viene calcolato il valore di E;
la variabile x viene provvisoriamente legata al valore di E;
tenendo conto di questo nuovo legame, viene calcolato il valore di F: questo il valore
dellintera espressione;
il legame provvisorio di x viene sciolto: x torna ad avere il valore che aveva prima o
nessun valore.

Valutazione di dichiarazioni locali


let fraction (n,d) =

let com = gcd(n,d)


in (n/com, d/com)

# fraction(32,28)

d
28
n
32
fraction function(n,d) => ...
...
...

let com = gcd(n,d)

com
4
d
28
n
32
fraction function(n,d) => ...
...
...

in (n/com, d/com)

il valore (8,7)

d
28
n
32
fraction function(n,d) => ...
...
...

- :

fraction function(n,d) => ...


...
...

int * int = 8,7

Dichiarazioni locali: osservazioni


Le variabili locali non hanno pi un valore dopo la valutazione dellespressione
# let n=3 in n*5;;
- : int = 15
# n;;
Characters 0-1:
n;;
^
Unbound value n
Il legame locale sovrascrive altri eventuali legami globali
Dichiarazioni locali nidificate
# let x=3*8
in let y=4+1
in x-y;;
- : int = 19

# let x=3*8
in let y=x+1 (* il valore di x e visibile *)
in (x+y) mod 5;; (* x+y=24+25 *)

Esercizio
Qual il valore dellespressione seguente?
let x = 3*8 in let x=x+1 in (x+x) mod 5
E qual il valore di x dopo la valutazione dellespressione?

Forma generale delle dichiarazioni locali


DICHIARAZIONE-LET
in ESPRESSIONE
unESPRESSIONE
Il suo valore il valore che ha ESPRESSIONE nellambiente che si ottiene estendendo
lambiente attuale mediante DICHIARAZIONE-LET
Per valutare
DICHIARAZIONE-LET
in ESPRESSIONE
in un ambiente E:
1. viene valutata DICHIARAZIONE-LET in E (lambiente E viene esteso)
2. viene valutata ESPRESSIONE nel nuovo ambiente
3. viene ripristinato lambiente E

Funzioni in forma currificata


Una funzione su tuple si pu riscrivere come una funzione che consuma un argomento alla
volta
let mult (m,n) = m * n;;
let times m n = m * n;;

mult
: int * int -> int
times
: int -> int -> int
times 5 :
int -> int

times la forma currificata di mult


times 5 unapplicazione parziale di times
sum (times 2) 1 10

calcola

10
X

(2 k)

k=1

In generale, fc la forma currificata di f se


f : t1 ... tn t
fc : t1 (t2 ... (tn t)...)
e per ogni a1, ..., an:
f (a1, ..., an) = (((fca1) a2) ...an)
Le parentesi possono essere omesse,
sia nel tipo di fc (si associa a destra),
sia nellapplicazione di fc (si associa a sinistra).

Espressioni per denotare funzioni


1. variabili (times)
2. astrazioni funzionali
(function x -> function y -> x*y,
o anche fun x y -> x*y)
3. espressioni funzionali (comp(double,plus 5))
Altri esempi
# let pair x y = (x,y);;
val pair : a -> b -> a * b = <fun>
# let lessthan x y = y < x;;
val lessthan : a -> a -> bool = <fun>
lessthan 0 un predicato: essere minore di 0
# let greaterthan x y = y > x;;
val greaterthan : a -> a -> bool = <fun>
# let equal x y = x=y;;
val equal : a -> a -> bool = <fun>
equal 8 un predicato: essere uguale a 8

Molte operazioni predefinite in Ocaml sono in forma currificata


# max;;
- : a -> a -> a = <fun>
Le operazioni infisse predefinite sono in forma currificata:
# ( * );;
- : int -> int -> int = <fun>
# (=);;
- : a -> a -> bool = <fun>

Definizione di operatori infissi


# let (@@) f g x = f(g x);;
val @@ : (a -> b) -> (c -> a) -> c -> b = <fun>
# double @@ treble;;
- : int -> int = <fun>
# @@;;
Characters 0-2:
Syntax error
# (@@);;
- : (a -> b) -> (c -> a) -> c -> b = <fun>

Gestione dei casi eccezionali


OCaml prevede un tipo di dati particolare, quello delle le ECCEZIONI:
exn
Le eccezioni consentono di scrivere programmi che segnalano un errore: cio di definire
funzioni parziali.
Procedura di definizione di una funzione parziale:
se caso particolare allora ERRORE
altrimenti ....
Esiste un insieme di eccezioni predefinite: Match_failure, Division_by_zero,...
Ma linsieme dei valori del tipo exn pu essere esteso, mediante la
dichiarazione di eccezioni:
(* dichiarazione di eccezione *)
exception NegativeNumber
Nomi delle eccezioni
Il nome di uneccezione deve iniziare con una lettera maiuscola

Come segnalare un errore


Dopo aver dichiarato uneccezione, leccezione pu essere sollevata:
(* fact: int -> int
fact n solleva leccezione NegativeNumber se n e minore di 1,
altrimenti riporta il fattoriale di n *)
let rec fact n =
if n < 0 then raise NegativeNumber
(* viene "sollevata" leccezione *)
else if n=0 then 1
else n * fact (n-1)
# fact 3;;
- : int = 6
# fact (-1);;
Exception: NegativeNumber.

Propagazione delle eccezioni


# 4 * fact (-1) ;;
Exception: NegativeNumber.
Se durante la valutazione unespressione E viene sollevata uneccezione, il calcolo del valore
di E termina immediatamente (il resto dellespressione non viene valutato), e viene sollevata
leccezione.
(* loop : a -> b *)
let rec loop n = loop n;;
# let f=fact(-1) in loop f;;
Exception: NegativeNumber.

Catturare uneccezione
Uneccezione pu essere catturata per implementare procedure di questo tipo:
calcolare il valore di E
se nel calcolo di tale valore si verifica un errore
allora .....
# try 4 * fact(-1)
with NegativeNumber -> 0;;
- : int = 0

Attenzione:
Se E di tipo exn:
1. E pu essere il valore di qualunque funzione
2. E pu essere argomento di qualunque funzione
Le eccezioni sono eccezioni alla tipizzazione forte.

Cos un pattern?
Un pattern una espressione costituita mediante variabili e costruttori di tipo.
Per i tipi introdotti fin qui, i costruttori sono tutti e solo:
i valori del tipo int, float, bool, char, string, unit
i costruttori di tuple (coppie, triple, ...), cio parentesi e virgole ( , ), ( , , ) ...
Esempi:
x un pattern, in quanto variabile;
"pippo" un pattern, in quanto valore, quindi costruttore del tipo string;
2.0 un pattern, in quanto valore, quindi costruttore del tipo float;
(x,y) un pattern, in quanto espressione ottenuta a partire da variabili e costruttori
del tipo coppia;
(0,x) un pattern, in quanto espressione ottenuta a partire da variabili, costruttori
del tipo int e costruttori del tipo coppia;
(x,true,y) un pattern, in quanto espressione ottenuta a partire da variabili,
costruttori del tipo bool e costruttori del tipo tripla;
x+y NON un pattern in quanto + non un costruttore;
-n

NON un pattern in quanto non un costruttore.

In un pattern non possono per esserci occorrenze multiple di una stessa


variabile (con uneccezione, la variabile muta): (x,x) NON un pattern

PATTERN MATCHING: confronto con un pattern


Un valore V conforme a un pattern P se possibile sostuire le variabili
in P con sottoespressioni di V in modo tale da ottenere V stesso.
Pattern matching: confronto di unespressione E con un pattern P :
il confronto ha successo se il valore V di E conforme al pattern P
in caso di successo, viene stabilito come sostituire le variabili del pattern P in modo da
ottenere V .

Esempi
Pattern
"pippo"
"pippo"
"pippo"
x
x
x
("pippo", true)
("pippo", true)
("pippo", x)
(n,m)
(n,m,k)
(n,m,k)

(n,(m,k))
(n,(m,k))

Espressione
"pippo"
"pluto"
0
"pippo"
"pi""ppo"
3*8
("pippo", true)
("pippo", 3<0)
("pippo", 3<0)
(2.0, 3<0)

Pattern Matching
Successo
Fallimento
ERRORE
x="pippo"
x="pippo"
x=24
Successo
Fallimento
x=false
n=2.0
m=false
(2.0, 3<0)
Fallimento
(2.0, 3*8, 3>0)
n=2.0
m=24
k=true
(2.0, 3*8, 3>0)
Fallimento
(2.0, (3*8, 3>0))
n=2.0
m=24
k=true

Nota : Il pattern matching di un pattern costituito da una sola variabile con qualunque
espressione, di qualunque tipo, ha sempre esito positivo.

Uso di pattern nelle dichiarazioni di valore


Quando si scrive una dichiarazione della forma
let x = <ESPRESSIONE>
la variabile x un pattern. La forma generale delle dichiarazioni di questo tipo :
let <PATTERN> = <ESPRESSIONE>
# let (x,y) = (3*8, (10<100 or false));;
val x : int = 24
val y : bool = true

Uso di pattern nelle espressioni funzionali


Anche nelle espressioni funzionali della forma
function x -> <ESPRESSIONE>
la variabile x un caso particolare di pattern.
Pi in generale, unespressione funzionale ha la forma:
function <PATTERN> -> <ESPRESSIONE>
Ad esempio, possiamo definire quorem cos:
let quorem =
function (n,m) -> (n/m, n mod m)

Chiamata di funzioni definite mediante luso di pattern


let sort (x,y) = if x<y then (x,y)
else (y,x);;
# sort (3*8,100/2);;
y
50
x
24
sort function(x,y) -> ...
...
...

sort (x,y)

- :

int * int = (24, 50)


sort function(x,y) => ...
...
...

Forma generale delle espressioni function


function P1 -> E1
| P2 -> E2
...
| Pn -> En
I pattern P1, ..., Pn devono essere tutti dello stesso tipo T1.
Le espressioni E1, ..., En devono essere tutte dello stessotipo T2.
Il tipo dellespressione function ... : T1 T2.

Valutazione dellapplicazione di funzioni definite


mediante espressioni function generali
let F = function P attern1 -> E1
| P attern2 -> E2
...
| P atternn -> En
Per valutare F(Expr) (cio il valore di una funzione F applicata ad una espressione Expr):
1. Viene calcolato il valore V dellargomento Expr.
2. Il valore V viene confrontato con P attern1, P attern2, ..., nellordine:
se il confronto d sempre esito negativo, la valutazione riporta un errore.
Altrimenti:
(a) Sia P atterni il primo pattern con cui i confronto di V ha successo; si aggiungono
provvisoriamente i nuovi legami determinati dal pattern matching.
(b) Con questi nuovi legami viene valutato il corpo della funzione Ei.
(c) Il valore di Ei viene riportato come valore di F(Expr).
(d) I legami provvisori vengono sciolti.

La variabile muta : esempi di pattern matching

Pattern
_
("pippo", _)
(n,_,m)
(_,_)
(_,_)

Espressione
"pippo"
("pippo", 3<0)
(2.0, 3*8, 3>0)

Pattern Matching
Successo
Successo
n=2.0
m=true
(2.0, 3*8, 3>0)
Fallimento
(2.0, (3*8, 3>0))
Successo

Espressioni match
match E with
P1 -> E1
| P2 -> E2
| ...
| Pk -> Ek
I pattern P1, ..., Pn devono essere tutti dello stesso tipo, e dello stesso tipo di E.
Le espressioni E1, ..., En devono essere tutte dello stesso tipo T .
Tipo dellespressione match ...: T .

Valutazione di espressioni match


let Expr = match F with

P attern1 -> E1
| P attern2 -> E2
...
| P atternn -> En

Per valutare Expr:


1. Viene valutata lespressione F
2. Il valore ottenuto viene confrontato con P attern1, P attern2, ..., nellordine;
se il confronto d sempre esito negativo, la valutazione riporta un errore.
Altrimenti:
(a) Sia P atterni il primo pattern con cui il valore di F si confronta positivamente: si
aggiungono provvisoriamente i nuovi legami determinati dal pattern matching.
(b) Con questi nuovi legami viene valutata lespressione Ei.
(c) Il valore di Ei viene riportato come valore di Expr.
(d) Vengono sciolti i legami provvisori.

Pattern non esaustivi


# let xor(p,q) = match p with
true -> not q;;
Characters 15-43:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
false
...............match p with
true -> not q..
val xor : bool * bool -> bool = <fun>
# xor(false,true);;
Exception: Match_failure ("", 15, 43).

Chiamata ricorsiva di funzioni


let rec fact n =
if n<=0 then 1
else n * fact (n-1)

fact function n -> ...


...
...

# fact 2
n * 1
fact n =
n*fact(n-1)

n
2
fact function n -> ...
...
...

n
1
n
2
fact function n -> ...
...
...

riporta 1
fact n =
n*fact(n-1)

n
1
n
2
fact function n -> ...
...
...

n * 1

n
2
fact function n -> ...
...
...

riporta 2
fact n = 1

n
0
n
1
n
2
fact function n -> ...
...
...

riporta 1

- :

int = 2

fact function n -> ...


...
...

VALUTAZIONE DI ESPRESSIONI
MODELLO DI CALCOLO:
CALCOLARE = RIDURRE
# let square x = x * x;;
val square : int -> int = <fun>
square (3*2) ==> square 6 ==> 6 * 6 ==> 36
square (3*2) ==> (3*2)*(3*2) ==> 6*(3*2) ==> 6*6 ==> 36
Regole di calcolo:
CALL BY VALUE: calcolare il valore dellargomento prima di applicare una funzione
CALL BY NAME: applicare la funzione prima di aver calcolato il valore dellargomento
Regola di calcolo di ML: call by value
Eccezioni: espressioni condizionali, operatori booleani
Efficienza?
# let zero x = 0;;
val zero : a -> int = <fun>
zero(square 6) ==> zero(6*6) ==> zero 36 ==> 0
zero(square 6) ==> 0
Non tutte le funzioni utilizzano sempre tutti i loro argomenti.

Necessit di espressioni lazy


# let rec fact n = if n<=0 then 1
else n * fact(n-1);;
val fact : int -> int = <fun>
# fact 3;;
- : int = 6
Call by value:
fact 1 ==> if 1 <= 0 then 1 else 1 * fact(1-1)
==> if false then 1 else 1 * fact 0
==> if false then 1
else 1 * (if 0<=0 then 1 else 0 * fact(0-1))
==> if false then 1
else 1 * (if true then 1 else 0 * fact(-1))
==> if false then 1
else 1 * (if true then 1
else 0 * (if -<=0 then 1 else fact(-1-1)))
==> if false then 1
else 1 * (if true then 1
else 0 * (if true then 1
else fact(-2)))
................

Potrebbero piacerti anche