Dichiarazioni locali Problema : dati tre numeri interi, n, m e k

1
Dichiarazioni locali
Problema : dati tre numeri interi, n, m e k, calcolare il quoziente e il resto della
divisione di n + m per k
Procedimento : sia somma il valore di n+m; riportare (somma/k, somma mod k)
In Ocaml:
let somma = n+m in (somma/k, somma mod k)
(* soluzione : int * int * int -> int * int *)
let soluzione(n,m,k) =
let somma = n+m
in (somma/k, somma mod k)
Dichiarazione locale di x in F:
let x = E in F
dove E e F sono espressioni.
2
let x = E in F è un’espressione: ha un tipo e un valore
Il tipo dell’espressione let x=E in F è il tipo di F.
Il suo valore è lo stesso valore che ha l’espressione F quando x è sostituito da E:
in particolare, quindi, il valore di
let somma = n+m in (somma/k, somma mod k)
è il valore di
((n+m)/k, (n+m) mod k)
3
Le dichiarazioni locali sono “zucchero sintattico”
La funzione definita utilizzando una dichiarazione locale
(* soluzione : int * int * int -> int * int *)
let soluzione(n,m,k) =
let somma = n+m
in (somma/k, somma mod k)
è equivalente alla funzione definita utilizzando una funzione “anonima”:
let soluzione(n,m,k) =
(function somma -> (somma/k, somma mod k)) (n+m)
let x=E in F ⇐⇒ (function x -> F) E
4
O anche alla funzione definita utilizzando una funzione ausiliaria:
(* quorem : int * int -> int * int *)
let quorem(m,n) =
(m/n, m mod n)
(* soluzione : int * int * int -> int * int *)
let soluzione(n,m,k) =
quorem(n+m,k)
5
Esercizio
Che differenza c’è tra la funzione prima definita
(* soluzione : int * int * int -> int * int *)
let soluzione(n,m,k) =
let somma = n+m
in (somma/k, somma mod k)
e quella seguente?
(* sol : int * int * int -> int * int *)
let sol(n,m,k) =
((n+m)/k, (n+m) mod k)
6
Variabili locali
Nell’espressione
let x = E in F
x è una variabile locale tale che :
• x ha un valore (quello dell’espressione E) soltanto all’interno dell’espressione F.
• quando tutta l’espressione 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
7
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 *)
8
Valutazione di una dichiarazione locale
Per valutare un’espressione 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 dell’intera espressione;
• il legame provvisorio di x viene sciolto: x torna ad avere il valore che aveva prima
o nessun valore.
9
Esempio: riduzione di una frazione n/d ai minimi termini
(* gcd : int * int -> int *)
let rec gcd (m,n) =
if n=m then n
else if n>m then gcd(n-m,m)
else gcd(n,m-n)
(* fraction : int * int -> int * int *)
let fraction(n,d) =
(n / gcd(n,d), d / gcd(n,d))
Problema: in fraction, il valore di gcd(n,d) viene calcolato due volte.
10
Esempio (segue)
• Una soluzione: definire una funzione ausiliaria
(* divideboth : int * int * int -> int * int *)
let divideboth(n,d,com) = (n/com, d/com);;
let fraction(n,d) = divideboth (n, d, gcd(n,d));;
È un modo di “dare il nome” com a gcd(n,d)
• Alternativa: uso di una dichiarazione di valore locale:
(* fraction : int * int -> int * int *)
let fraction (n,d) =
let com = gcd(n,d)
in (n/com, d/com)
# fraction(32,28);;
- : int * int = (8, 7)
11
Valutazione di dichiarazioni locali
let fraction (n,d) =
let com = gcd(n,d)
in (n/com, d/com)
12
# 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
13
Dichiarazioni locali: osservazioni
• Le variabili “locali” non hanno più un valore dopo la valutazione
dell’espressione
# 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
Esercizio
# let x=3*8
in let y=x+1 (* il valore di x e’ visibile *)
in (x+y) mod 5;; (* x+y=24+25 *)
14
Qualè il valore dell’espressione seguente?
let x = 3*8 in let x=x+1 in (x+x) mod 5
E qual è il valore di x dopo la valutazione dell’espressione?
15
Forma generale delle dichiarazioni locali
DICHIARAZIONE-LET
in ESPRESSIONE
• È un’ESPRESSIONE
• Il suo valore è il valore che ha ESPRESSIONE nell’ambiente che si ottiene estendendo
l’ambiente attuale mediante DICHIARAZIONE-LET
• Per valutare
DICHIARAZIONE-LET
in ESPRESSIONE
in un ambiente E:
1. viene “valutata” DICHIARAZIONE-LET in E (l’ambiente E viene esteso)
2. viene valutata ESPRESSIONE nel nuovo ambiente
3. viene ripristinato l’ambiente E
16
Dichiarazione locale di funzioni
Utilizzate spesso quando funzioni ausiliarie non hanno significato al di fuori del programma principale.
Esempio: nella riduzione di una frazione ai minimi termini
(* gcd: int * int -> int *)
let rec gcd(n,m) = ....
(* divideboth : int * int * int -> int * int *)
let divideboth(n,d,com) = (n/com, d/com);;
let fraction(n,d) = divideboth (n, d, gcd(n,d));;
la funzione gcd: int * int -> int ha un significato autonomo: è preferibile non
definirla localmente.
Ma possiamo definire:
(* fraction: int * int -> int * int *)
let fraction(n,d) =
(* divideboth : int * int * int -> int * int *)
let divideboth(n,d,com) = (n/com, d/com)
in divideboth (n, d, gcd(n,d));;
17
Dichiarazione locale di funzioni (II)
Dichiarazione locale di funzioni quando:
• La funzione defnita localmente non ha significato autonomo
• La dichiarazione locale permette di “risparmiare parametri”
Esempio: è possibile definire divedeboth con un solo argomento:
let fraction(n,d) =
(* divideboth: int -> int * int *)
let divideboth com = (n/com, d/com)
in divideboth (gcd(n,d));;