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));;