Variabili Globali
Le variabili globali sono variabili che possono venire usate da tutte le funzioni
intendiamo definire e dal programma principale stesso.
Si tratta di variabili che sono definite al di fuori del corpo di ogni funzione, e che
sono accessibili da tutti i “blocchi” del programma.
Per dichiarare una variabile globale, si mette la dichiarazione prima della
definizione di ogni funzione.
Si osservi che i nostri programmi possono contenere una sola variabile globale con
un dato nome, diciamo n, mentre possono contenere molteplici variabili locali con
lo stesso nome (n), dato che ciascuna di queste risiede in un differente contesto
lessicale.
Come vedremo, la funzione SETQ consente di creare variabili globali.
1
La funzione predefinita: SETQ
In LisP è possibile stabilire il valore di un “atomo” mediante l’impiego di una
funzione speciale, la funzione SETQ. Il suo effetto è quello di trasformare il valore
del suo primo argomento nel valore del suo secondo argomento.
Vediamo alcuni esempi,
CG-USER(1):
CG-USER(2):
(SETQ ZERO 0)
(0)
(SETQ classifica ‘(milan juve lazio))
(MILAN JUVE INTER)
La funzione predefinita SETQ consente l’assegnazione simultanea di più variabili,
ad esempio,
CG-USER(3):
(SETQ pigreco 3.14 radicedidue 1.41 a pigreco)
(3.14)
... naturalmente anche gli altri valori sono stati assegnati.
In quest’ultimo caso, la funzione SETQ accetta n coppie di operandi e assegna ad
ogni primo elemento di una coppia il valore del secondo elemento corrispondente.
2
Come usiamo SETQ
Uno degli impieghi che possiamo fare della funzione SETQ è quello di creare,
all’inizio del nostro programma, alcune variabili globali, i cui valori possano
servirci, ad esempio, come valori di input per il programma stesso.
Ad esempio,
(SETQ
(SETQ
(SETQ
(SETQ
(SETQ
string1
string2
string3
string4
string5
'(a b c d e 8 e d c b a))
'(Madam Im Adam))
'(A man a plan a canal Panama))
'(rats live on no evil star))
'(Sit on a potato pan otis))
(defun PPal(inlist)
(cond
((null inlist) nil)
.
.
.
CG-USER(1):
CG-USER(2):
(Ppal string3) . . .
(car string4)
RATS
3
Variabili Locali
Ogni variabile ha uno scope, che possiamo identificare con la porzione di codice
nel quale è contenuta la variabile.
Le variabili che compaiono fra gli argomenti di una funzione, dato che il loro scope
è ristretto al corpo della funzione, sono dette variabili locali.
Si consideri l’esempio seguente:
(defun DOUBLE (n)
(* n 2 ))
Si osservi che, ad ogni chiamata della funzione DOUBLE, viene creata una nouva
variabile locale di nome n.
Dentro il corpo della funzione DOUBLE, il nome n si riferisce a questa variabile,
ma, fuori dal corpo della funzione, può accadere che n sia il nome di un’altra
variabile.
4
La funzione predefinita: LET
La funzione predefinita LET ha come obiettivo la creazione di variabili locali. La
sintassi generale della funzione è la seguente,
(LET
( (var-1 value-1)
(var-2 value-2)
.
.
.
(var-n value-n) )
body)
Il corpo di LET segue esattamente le stesse regole del corpo di una qualunque
funzione.
5
Un esempio per LET
Quello che segue è un semplice esempio dell’uso della funzione LET per la
creazione di variabili locali.
Si osservi che la variabile SUM è “locale”, poichè la porzione di codice nella quale è
contenuta (il suo scope), non è altro che il corpo della funzione MEDIA... fuori da
questa funzione, SUM potrebbe non avere il significato che abbiamo inteso
assegnarle.
CG-USER(1):
(defun MEDIA (x y)
(let
((sum (+ x y)))
(list x y 'media= (/ sum 2.0))))
CG-USER(2):
(media 1 10)
(1 10 MEDIA= 5.5)
6
... con più di una variabile locale
L’esempio seguente mostra come sia possibile creare simultaneamente più d’una
variabile locale,
CG-USER(1):
(defun duetto (x)
(let
((star (first x))
(co-star (third x)))
(list co-star 'accompagnata 'da star)))
CG-USER(2):
(duetto '(fred e ginger))
(GINGER ACCOMPAGNATA DA FRED)
7
La funzione predefinita: LET*
La funzione predefinita LET* è simile alla funzione LET ed ha il medesimo
obiettivo: la creazione di variabili locali. L’unica differenza fra le due funzioni
risiede nel fatto che LET* crea variabili locali in sequenza, consentendo
l’utilizzazione delle variabili appena create.
Ad esempio,
CG-USER(1):
(defun cambia-prezzo (old new)
(let*
((diff (- new old))
(proporzione (/ diff old))
(percentuale (* proporzione 100.0)))
(list ‘il ‘prezzo ‘è ‘variato ‘del percentuale ‘%)))
CG-USER(2):
(cambia-prezzo 5 15)
(IL PREZZO E’ VARIATO DEL 200.0 %)
Questo metodo per creare varibili locali è utile ogni volta che si intende assegnare
un nome a diversi passi intermedi di una lunga computazione.
8
(I) Differenze fra LET e LET*...
L’esempio seguente mostra un uso scorretto della funzione LET per la creazione di
variabili locali.
Sapreste indicare il perchè l’esempio provoca un malfunzionamento del
programma?
CG-USER(1):
(defun faulty-size-range (x y z)
(let
((biggest (max x y z))
(smallest (min x y z))
(r (/ biggest smallest 1.0)) )
(list 'factor 'of r) ))
CG-USER(2):
(faulty-size-range 34 35 20)
Error: Attempt to take the value of the
unbound variable `BIGGEST'
9
Il tracciato della valutazione di faulty-size-range
(faulty-size-range 35 87 4)
Enter FAULTY-SIZE-RANGE with inputs 35, 87, and 4
create variables X, Y, and Z, with values 35, 87, and 4
(let ...)
(max x y z)
87
(min x y z)
4
(/ biggest smallest 1.0)
Error! BIGGEST unassigned variable
10
(II) Differenze fra LET e LET*...
L’esempio seguente mostra come assegnare correttamente i valori a più variabili,
sfruttando la capacità della funzione LET* di creare le varibili in sequenza.
CG-USER(3):
(defun correct-size-range (x y z)
(let*
((biggest (max x y z))
(smallest (min x y z))
(r (/ biggest smallest 1.0)) )
(list 'factor 'of r) ))
CG-USER(4):
(correct-size-range 34 35 20)
(FACTOR OF 1.75)
11
Il tracciato della valutazione di correct-size-range
(correct-size-range 35 87 4)
Enter CORRECT-SIZE-RANGE with inputs 35, 87, and 4
create variables X, Y, and Z, with values 35, 87, and 4
(let* ...)
(max x y z)
87
create variable BIGGEST, with value 87
(min x y z)
4
create variable SMALLEST, with value 4
(/ biggest smallest 1.0)
21.75
create variable R, with value 21.75
(list ’factor ’of r)
(FACTOR OF 21.75)
Result of LET* is (FACTOR OF 21.75)
Result of CORRECT-SIZE-RANGE is (FACTOR OF 21.75)
12
Il Crivello di Eratostene
Algoritmo che consente di individuare tutti i numeri primi minori di
un dato numero
Supponiamo di voler trovare tutti i numeri primi minori o uguali ad n. Assai poco
formalmente, diciamo che l’algoritmo procede manipolando la lista dei numeri che
vanno da 3 a n (il caso in cui n è uguale a 1 ed n uguale a 2,rappresentano i casi
di terminazione dell’algoritmo).
Ricorsivamente, il problema viene decomposto in problemi più semplici: per liste
di lunghezza n-1, n-2, n-3, ..., fino alla “base” della definizione.
La ricostruzione della lista, dunque, avviene testando se ogni nuovo elemento che
deve essere aggiunto è o meno multiplo di uno almeno dei numeri già presenti,
nel qual caso viene omesso dalla ricostruzione e si passa a testare il numero
successivo. Il controllo su n rappresenta l’ultimo passo della computazione.
La lista che in tal modo si ottiene non contiene alcun numero che risulti multiplo di
altri numeri (nessun numero precedente lo divide)... ogni elemento della lista è un
numero primo !
13
CG-USER(1):
(funf 7)
0[4]: (FUNF 7)
1[4]: (FUNF 6)
2[4]: (FUNF 5)
3[4]: (FUNF 4)
4[4]: (FUNF 3)
5[4]: (FUNF 2)
5[4]: returned (2)
5[4]: (FUNG 3 (2))
6[4]: (FUNH 3 (2))
7[4]: (FUNK 2 3)
7[4]: returned NIL
7[4]: (FUNH 3 NIL)
7[4]: returned NIL
6[4]: returned NIL
5[4]: returned (3 2)
4[4]: returned (3 2)
4[4]: (FUNG 4 (3 2))
5[4]: (FUNH 4 (3 2))
6[4]: (FUNK 3 4)
6[4]: returned NIL
6[4]: (FUNH 4 (2))
7[4]: (FUNK 2 4)
7[4]: returned T
6[4]: returned T
5[4]: returned T
4[4]: returned (3 2)
3[4]: returned (3 2)
3[4]: (FUNG 5 (3 2))
4[4]: (FUNH 5 (3 2))
5[4]: (FUNK 3 5)
5[4]: returned NIL
5[4]: (FUNH 5 (2))
6[4]: (FUNK 2 5)
6[4]: returned NIL
...
...
...
...
...
...
6[4]: (FUNH 5 NIL)
6[4]: returned NIL
5[4]: returned NIL
4[4]: returned NIL
3[4]: returned (5 3 2)
2[4]: returned (5 3 2)
2[4]: (FUNG 6 (5 3 2))
3[4]: (FUNH 6 (5 3 2))
4[4]: (FUNK 5 6)
4[4]: returned NIL
4[4]: (FUNH 6 (3 2))
5[4]: (FUNK 3 6)
5[4]: returned T
4[4]: returned T
3[4]: returned T
2[4]: returned (5 3 2)
1[4]: returned (5 3 2)
1[4]: (FUNG 7 (5 3 2))
2[4]: (FUNH 7 (5 3 2))
3[4]: (FUNK 5 7)
3[4]: returned NIL
3[4]: (FUNH 7 (3 2))
4[4]: (FUNK 3 7)
4[4]: returned NIL
4[4]: (FUNH 7 (2))
5[4]: (FUNK 2 7)
5[4]: returned NIL
5[4]: (FUNH 7 NIL)
5[4]: returned NIL
4[4]: returned NIL
3[4]: returned NIL
2[4]: returned NIL
1[4]: returned (7 5 3 2)
0[4]: returned (7 5 3 2)
(7 5 3 2)
14
L’algoritmo di Eratostene
(defun FUNF (n)
(cond
((<= n 1) nil)
((= n 2) '(2))
(t (FUNG n (FUNF (1- n)))) ))
La funzione FUNF è la funzione
principale; definisce la “base” (cioè, le
condizioni di terminazione per la
ricorsione) ed il “passo” ricorsivo.
“1- n” e “1+ n” sono funzioni predefinite
di incremento e decremento di uno.
(defun FUNG (n ps)
(cond
((FUNH n ps) ps)
(t (cons n ps)) ))
Se il controllo eseguito dalla funzione
FUNH è positivo - cioè se n è multiplo di
qualche numero già presente nella lista la funzione FUNG restituisce la lista
senza n; restituisce una nuova lista con in
testa n, altrimenti.
(defun FUNH (n p)
(cond
((null p) nil)
((numberp p) (FUNK p n))
((FUNK (car p) n) t)
(t (FUNH n (cdr p))) ))
La funzione FUNH, a valori nell’insieme
dei booleani {T, NIL}: ritorna il vero, se n
è multiplo di qualche numero già
presente nella lista, il falso, altrimenti. Il
controllo che svolge riguarda il numero n
la testa della lista di numeri che,
ricorsivamente, è stata decomposta e “in
parte” ricostruita. FUNH esegue il
controllo su tutti glli elementi della lista
(ricorsione sulla coda).
(defun FUNK (n m)
(cond
((integerp (/ m n)) t)
(t nil) ))
La funzione FUNK, a valori nell’insieme
dei booleani {T, NIL}: ritona il vero, se il
suo secondo argomento è multiplo del
primo; il falso, altrimenti.
Si osservi l’uso di integerp: non vale il
metodo tradizionale per testare se 15
n
divide m.
Scarica

var-globali