Bibliografia
1
Scheme ∗
(per principianti)
quaderno 1
2 gennaio 2014
e-mail: [email protected]
Bibliografia
3
∗. This document has been written using the GNU TEXMAC S text editor (see www.texmacs.org).
Indice
Glossario
. . . .. . . . .. . . .. . . . .. . . .. . . . .. . . .. . . . .. . . .. . . 7
1 Introduzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.1 Primi passi . . . . . . . . . . .
1.2 Le espressioni . . . . . . . . .
1.3 Le procedure . . . . . . . . . .
1.4 Espressioni numeriche . . . .
1.5 Le liste . . . . . . . . . . . . .
1.6 Esercizi . . . . . . . . . . . . .
1.7 Le espressioni contenenti let
1.8 Espressione lambda . . . .
1.9 Define . . . . . . . . . . . . . .
1.10 Espressioni con condizione
1.10.1 Esercizi . . . . . . . . .
1.11 Ricorsione . . . . . . . . . . .
1.12 Assegnamento . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. 9
10
11
13
14
18
24
27
32
38
42
43
49
Indice analitico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
Bibliografia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
5
Glossario
commento . . . . . . . . . . . . . . . . . . . . .
;
define
definisce una procedura o il valore di una variabile
definisce una lista . . . . . . . . . . . . . . . . .
accento ’
quote <lista>
definisce una lista . . . . . . . . . . . . . . . . .
car <lista>
estrae la testa di <lista> . . . . . . . . . . . . .
cdr <lista>
estrae la coda di una lista . . . . . . . . . . . . .
cons <oggetto > <lista> aggiunge, in testa, l’oggetto alla lista . . . . .
simbolo . . . . . . . . . . . . . . . . . . . . . . .
’a
coppia . . . . . . . . . . . . . . . . . . . . . . .
(a . b)
(let (assegnazioni) (corpo)) . . . . . . . . . . . .
let
espressione lambda
(lambda (var1 var2 .....) esp.1 esp.2 .....) . . . . . .
(if test espr_test_vero espr_test_falso) . . . . .
if
(and expr1 expr2 expr3 ......) . . . . . . . . . . .
and
(or expr1 expr2 expr3 .......) . . . . . . . . . . . .
or
cond
(cond (test exp.) (test exp.) ...... (else exp.)) . . .
7
. .
.
. .
. .
. .
. .
. .
. .
. .
. .
. .
. .
. .
. .
. .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
10
11
14
14
15
15
15
16
16
24
27
38
40
40
41
Capitolo 1
Introduzione
1.1 Primi passi
Per scrivere questo documento useremo Texmacs ho infatti scoperto che Scheme
può essere usato all’interno di Texmacs, basta aprire una sessione di scheme
premendo
e scegliendo
scheme
si ottiene questo:
Scheme] (+ 22 3)
25
2 msec
Scheme] (+ 2 5 7); somma multipla
14
Scheme] ;; commento
(* 3 5)
15
Scheme]
9
10
Introduzione
I commenti si ottengono usando il ;
Il testo di riferimento é il seguente Scheme tutorial [1, The Scheme programming language] facilmente reperibile su Internet.
1.2 Le espressioni
Una espressione é una cosa di questo tipo:
Scheme] (define a 10)
Scheme] (+ a 5)
15
Scheme] (* a a)
100
Scheme]
Ad a viene attribuito il valore 10
Scheme] (+ a a)
20
Scheme] ;possiamo ridefinire la a
#<eof>
Scheme] (define a 8)
Scheme] (* a a)
64
Scheme]
Si può anche lavorare sui caratteri, ad esempio
Scheme] (define stringa ’(a b c d e))
Scheme] (car stringa)
a
Scheme] (cdr stringa)
(b c d e)
Scheme] (define stringabis ’(x y z))
Scheme] (cons stringa stringabis)
((a b c d e) x y z)
Scheme] (car (cons stringa stringabis))
(a b c d e)
Scheme]
1.3 Le procedure
11
Gli esempi precedenti illustrano bene come manipolare le stringhe e i caratteri, non sono esempi dettagliati ma le regole da seguire nell’usare le funzioni
citate mi sembrano facilmente intuibili, maggiori spiegazioni potrebbero apparire pedanti, l’unica cosa da sottolineare é l’ultimo esempio dove il risultato
(output) di una funzione viene usato come parametro (imput) di un’altra funzione.
1.3 Le procedure
Cominciamo con il vedere qualche cosa sulle procedure iniziando a definire la
procedura quadrato.
Scheme] (define quadrato (lambda (n)(* n n)))
Scheme] ;; usiamo la procedura appena definita
#<eof>
Scheme] (quadrato 3)
9
Scheme]
Le procedure possono anche avere più parametri, ad esempio:
Scheme] (define area_rettangolo (lambda (altezza base)(* base
altezza)))
Scheme] (area_rettangolo 20 10)
200
Scheme]
Le regole sintattiche cominciano a diventare chiare e semplici:
•
•
Ogni istruzione deve essere compresa tra parentesi rotonde
Gli operatori sono sempre prefissi, tra operatori e operandi e tra gli operandi ci vogliono degli spazi.
Le procedure possono richiamare altre procedure, esempio:
Scheme] (define volume_parallelepipedo
(lambda (base spessore altezza)
(* (area_rettangolo base spessore) altezza)))
Scheme] (volume_parallelepipedo 10 20 30)
6000
Scheme]
Vediamo un altro esempio significativo:
Scheme] (define reciproco (lambda (n)
(if (= n 0) "infinito" (/ 1 n))))
Scheme] (reciproco 4)
12
Introduzione
1/4
Scheme] (reciproco 0)
"infinito"
Scheme]
Se dopo aver scritto l’esempio precedente voi chiudete Texmacs e successivamente lo riavviate succede una cosa di questo tipo:
Scheme] (reciproco 4)
unbound-variable
Scheme]
La funzione reciproco non esiste più, per riottenerla si può procedere nel
seguente modo:
•
andate nel blocco dove essa é definita e premete il tasto destro
1.4 Espressioni numeriche
•
scegliete Evaluate all
•
infine provate a ripetere l’operazione che prima non era riuscita
13
Scheme] (reciproco 4)
1/4
Scheme] (reciproco 1/10)
10
Scheme] (reciproco (reciproco 2/4))
1/2
Scheme] 2/4
1/2
Scheme]
e tutto torna a funzionare.
1.4 Espressioni numeriche
In scheme la gamma di numeri che si possono usare é molto ampia, si possono
usare:
−
−
−
−
numeri interi relativi e non
frazioni
numeri con la virgola
numeri complessi
di seguito sono riportate espressioni che usano questi numeri
Scheme] (+ 1/2 1/3)
5/6
Scheme] (* 3/4 16/9)
4/3
Scheme] (+ -3/5 -1/5)
-4/5
Scheme] (- 1.5 -3/2)
3.0
Scheme] (- -3/2 +1.5)
-3.0
14
Introduzione
Scheme] (+ 3+2i 7-4i)
10.0-2.0i
Scheme] (* 3 6-4i)
18.0-12.0i
Scheme] (* 3+2i 3-2i)
13.0
Scheme] (* 3-4i 3-4i)
-7.0-24.0i
Scheme]
1.5 Le liste
La lista é un tipo di dati strutturati simile al vettore (array) del C , Pascal,
Java. La lista si indica nel seguente modo
Scheme] ’(1 3 56) ; lista di numeri
(1 3 56)
Scheme] ’(a c g h j) ; lista di caratteri
(a c g h j)
Scheme]
Vi é un secondo metodo per indicare le liste:
Scheme] (quote (x r t s))
(x r t s)
Scheme] (quote ("as" "ghj" "contorno")); lista di stringhe
("as" "ghj" "contorno")
Scheme]
Questo secondo metodo é evidentemente più scomodo del precedente.
Si possono anche formare liste di liste:
Scheme] ’(’(a c d) ’(1 4 6) ’() ’("ancora" "certo"))
((quote (a c d)) (quote (1 4 6)) (quote ()) (quote ("ancora"
"certo")))
Scheme]
Si possono formare delle liste miste con oggetti di tipo diverso:
1.5 Le liste
15
Scheme] ’(1 a "ancora" ’(a b c))
(1 a "ancora" (quote (a b c)))
Scheme]
Le liste hanno una testa e una coda, la testa non é una lista ma é costituita
dal primo elemento, la coda é una lista ed é formata da tutti gli altri elementi
tranne il primo.
Per ottenere la testa si usa la funzione car , per ottenere la coda si usa la
funzione cdr .
Scheme] (define lista1 ’(a b c))
Scheme] (car lista1)
a
Scheme] (cdr lista1)
(b c)
Scheme] (cdr (cdr (cdr lista1)))
()
Scheme] (car (cdr lista1))
b
Scheme] (define lista2 (cdr lista1))
Scheme] (cdr lista2)
(c)
Scheme]
Per unire due liste si usa la funzione cons
Scheme] (define lista1 ’(a b c d))
Scheme] (define lista2 ’(1 2 3 4))
Scheme] (cons lista1 lista2)
((a b c d) 1 2 3 4)
Scheme]
Cons forma una lista il cui primo oggetto , la testa, é lista1 e il resto della
lista , la coda, é lista2, mi viene il sospetto che cons si possa usare con molta
disinvoltura:
Scheme] (cons a b)
unbound-variable
16
Introduzione
Scheme] ; non funziona, se mettiamo ’a e ’b forse li scambia per dei
caratteri.
#<eof>
Scheme] (cons ’a ’b)
(a . b)
Scheme] (pair? (cons ’a ’b))
#t
Scheme] ;Uau (a . b) e’ un tipo di lsta chiamata coppia ,sulle
coppie si possono fare delle bellissime cose ma lo vedremo
piu’ tardi.
#<eof>
Scheme] (cons ’a lista2)
(a 1 2 3 4)
Scheme] ; ’a è un carattere che viene aggiunto in testa alla lista2
#<eof>
Scheme] (cons (car lista1) lista2)
(a 1 2 3 4)
Scheme] (cons (car (cdr lista1)) lista2)
(b 1 2 3 4)
Scheme]
La funzione cons sembra essere molto complicata, non consente di unire due
liste ma aggiunge un oggetto ad una lista. Le righe di codice precedenti ci
hanno insegnato due cose:
•
•
la lettera a é una variabile, mentre ’a é un simbolo
(a . b) é una coppia
Quindi esistono i simboli e le variabili, che cosa sarà ’parola ?
Scheme] ’parola
parola
Scheme] (define qualchecosa ’parola)
Scheme] (car qualchecosa)
wrong-type-arg
Scheme] qualchecosa
parola
Scheme] (string? qualchecosa)
17
1.5 Le liste
#f
Scheme] (symbol? qualchecosa)
#t
Scheme]
é un simbolo e null’altro.
Le liste tipo (a . b) sono molto particolari:
Scheme] (define coppia ’(a . b))
Scheme] (car coppia)
a
Scheme] (cdr coppia)
b
Scheme] ;osservare che b e’
l’oggetto ’b non la lista (b)
#<eof>
Scheme] (string? (cdr coppia))
#f
Scheme] (char? (cdr coppia))
#f
Scheme] (symbol? (cdr coppia))
#t
Scheme] (symbol? ’b)
#t
Scheme] (symbol? b)
unbound-variable
Scheme] (symbol? ’simbolo)
#t
Scheme]
Come si vede il concetto di simbolo é molto gettonato, entra in gioco
questa nuova categoria, ed é un qualche cosa da porre accanto al concetto di
numero, numero complesso, stringa, lista, variabile, nome di funzione, etc...
vedremo sicuramente meglio in seguito di che cosa si tratta.
Vediamo ancora qualche cosa sulle liste con il . :
Scheme] (define lista-punto ’(a b c d e . f))
Scheme] (car lista-punto)
18
Introduzione
a
Scheme] (cdr lista-punto)
(b c d e . f)
Scheme] (define lista-punto2 ’(a b c . d e f))
read-error
Scheme] (define lista-punto2 ’(a b c . ’(d e f))
Scheme] (car lista-punto2)
)
a
Scheme] (cdr lista-punto2)
(b c quote (d e f))
Scheme]
1.6 Esercizi
Esercizio 1.1. Valutare con Scheme la seguenti espressioni
2
4 5
4
a) 3 + 9 11 − 9
Scheme] (* (+ 2/3 4/9) (- 5/11 4/9))
10/891
Scheme]
1
b) 1.2 2 − 3 + (−8.7)
Scheme] (+ (* 1.2 (- 2 1/3)) -8.7)
-6.7
Scheme]
c) 1 +
1
1
2+
1+
1
2
Scheme] (+ 1 (/ 1 (+ 2 (/ 1 (+ 1 1/2)))))
11/8
Scheme]
d) 1 · (−2) ·3· (−4) ·5· (−6) ·7
Scheme] (* 1 (* -2 (* 3 (* -4 (* 5 (* -6 7))))))
-5040
Scheme]
Esercizio 1.2. Fai degli esperimenti con le procedure + - * / usando argomenti numerici di
diverso tipo e ricava le regole seguite da Scheme.
1.6 Esercizi
Scheme] (+ 1 2)
3
Scheme] (+ 1.0 2.0)
3.0
Scheme] ; osservare che quando si usano i decimali anche il valore della
procedura e’ decimale
#<eof>
Scheme] (+ 1 2.0)
3.0
Scheme] ; e’ sufficiente che uno solo sia decimale
#<eof>
Scheme] (- 3 9)
-6
Scheme] (+ 3 1/3)
10/3
Scheme] ; se uno degli argomenti e’ frazionario anche + e’ frazionario
#<eof>
Scheme] (+ 1 2 3 4)
10
Scheme] ; gli argomenti di + possono essere piu’ di due
#<eof>
Scheme] (* 1 2 3 4 5 6 7)
5040
Scheme] ; anche quelli di *
#<eof>
Scheme] (- 10 9 7)
-6
Scheme] ; anche quelli di #<eof>
Scheme] (/ 10 2 5)
1
Scheme] ; anche quelli di /
#<eof>
Scheme] (/ 5 2)
5/2
Scheme] (/ 5 4 2)
5/8
Scheme] (/ 10.0 3)
3.33333333333333
Scheme] 3+2i
19
20
Introduzione
3.0+2.0i
Scheme] (+ 3 3+2i)
6.0+2.0i
Scheme] (/ 10-4i 2)
5.0-2.0i
Scheme] (/ 10-4i 2.0)
5.0-2.0i
Scheme] (+ 1/4+5/6i 1/2)
0.75+0.833333333333333i
Scheme] (+ 1/4+5/6i 1/2+0i)
0.75+0.833333333333333i
Scheme]
Esercizio 1.3. Determinare il valore delle seguenti espressioni:
a) (cons ’car ’cdr)
Scheme] (cons ’car ’cdr)
(car . cdr)
Scheme] ; coppia con punto
#<eof>
Scheme]
b) (list ’this ’(is silly))
Scheme] (list ’this ’(is silly))
(this (is silly))
Scheme]
c) (cons ’is ’(this silly?))
Scheme] (cons ’is ’(this silly?))
(is this silly?)
Scheme]
d) (quote (+ 2 3))
Scheme] (quote (+ 2 3))
(+ 2 3)
Scheme]
e) (cons ’+ ’(2 3))
Scheme] (cons ’+ ’(2 3))
(+ 2 3)
Scheme]
f) (car ’(+ 2 3))
21
1.6 Esercizi
Scheme] (car ’(+ 2 3))
+
Scheme]
g) (cdr ’(+ 2 3))
Scheme] (cdr ’(+ 2 3))
(2 3)
Scheme]
h) cons
Scheme] cons
#<primitive-procedure cons>
Scheme]
i) (quote cons)
Scheme] (quote cons)
cons
Scheme] (quote (cons))
(cons)
Scheme] (car (quote cons))
wrong-type-arg
Scheme] (car (quote (cons)))
cons
Scheme]
j) (quote (quote cons))
Scheme] (quote (quote cons))
(quote cons)
Scheme] (car (quote (quote cons)))
quote
Scheme]
k) (+ 2 3)
Scheme] (+ 2 3)
5
Scheme]
l) (+ ’2 ’3)
Scheme] (+ ’2 ’3)
5
Scheme]
m) (+ (car ’(2 3)) (car (cdr ’(2 3))))
22
Introduzione
Scheme] (+ (car ’(2 3)) (car (cdr ’(2 3))))
5
Scheme]
n) ((car (list + - * / )) 2 3)
Scheme] ((car (list + - * /)) 2 3)
5
Scheme]
Esercizio 1.4. L’espressione (car (car ’((a b) (c d)))) produce a , verificarlo e stabilire
quale combinazioni di car e cdr producono rispettivamente: b , c , d .
Scheme] (define lista ’((a b) (c d)))
Scheme] (car (car lista)); verifica
a
Scheme] (car
lista)
(a b)
Scheme] (car (car ’(’(a b) ’(c d))))
quote
Scheme] (car
(a b))
unbound-variable
Scheme] (list? (car lista))
#t
Scheme] (list? (car ’(’(a b) ’(c d))))
#t
Scheme]
Scheme]
Scheme]
Scheme]
(define lista1 ’(a b))
(define lista2 ’(c d))
(define lista3 ’(lista1 lista2))
(car (car lista3))
wrong-type-arg
Scheme] (car lista3)
lista1
Scheme] (list? (car lista3))
#f
Scheme] (list? lista1)
#t
Scheme] (symbol? (car lista3))
#t
Scheme] (list? (car (car ’(’(a b) ’(c d)))))
#f
Scheme]
23
1.6 Esercizi
Questa parte dell’esercizio introduce parecchi dubbi, intanto ’((a b) (c d)) non é la
stessa cosa di ’(’(a b) ’(c d)) , almeno per quanto riguarda la funzione car , non solo ma
definendo lista1 –> ’(a b) , lista2 –> ’(c d) e lista3 –> ’(lista1 lista2) si ottiene che
lista3 non é la stessa cosa di ’(’(a b) ’(c d)) e nemmeno di ’((a b) (c d)) , evidentemente
sulle liste vi sono ancora molte cose da imparare perché la logica che sta dietro a questo
concetto é per ora molto confusa.
Vediamo se riusciamo a svolgere la seconda parte dell’esercizio:
Scheme] (car (cdr (car lista))); primo risultato
b
Scheme] (car (car (cdr lista))); secondo risultato
c
Scheme] (car (cdr (car (cdr lista)))); terzo risultato
d
Esercizio 1.5. Scrivi i passi necessari per valutare le seguenti espressioni:
a) ((car (cdr (list + - * /))) 17 5)
I. (list + - * /) é la lista (+ - * /)
II. (cdr (list + - * /)) produce la lista (- * /)
III. (car (cdr (list + - * /)))
Scheme] ((car (cdr (list + - * /))) 17 5)
12
Scheme]
b) (cons (quote -) (cdr (quote (+ b c))))
I. (cdr (quote (+ b c)) produce la lista (b c)
II. Quindi il risultato sarà (- b c)
Scheme] (cons (quote -) (cdr (quote (+ b c))))
(- b c)
Scheme]
c) (cdr (cdr ’(a b c)))
I. (cdr ’(a b c)) produce la lista (b c)
II. Il risultato sarà quindi la lista (c)
Scheme] (cdr (cdr ’(a b c)))
(c)
Scheme] (cdr (cdr (quote (a b c))))
(c)
Scheme] ; equivalenza tra
quote
e
’
d) (cons ’d (cdr (cdr ’(a b c d e f))))
I. (cdr (cdr ’(a b c d e f))) produce la lista (c d e f)
24
Introduzione
II. Il risultato sarà quindi la lista (d c d e f)
Scheme] (cons ’d (cdr (cdr ’(a b c d e f))))
(d c d e f)
Scheme]
e) (cons (+ ’2 1/2) (list (- ’3 1/3) (+ ’4 1/4)))
1.1
1 5
=
2 2
1 8
II. (- ’3 1/3) produce 3 − =
3 3
1 17
III. (+ ’4 1/4) produce 4 + =
4
4
I. (+ ’2 1/2) produce 2 +
IV. Il risultato sarà dunque la lista
5 8 17
2 3 4
Scheme] (cons (+ ’2 1/2) (list (- ’3 1/3) (+ ’4 1/4)))
(5/2 8/3 17/4)
Scheme]
1.7 Le espressioni contenenti let
Consideriamo la seguente espressione:
Scheme] (+ a b)
unbound-variable
Scheme] ;non funziona, ma se scriviamo
#<eof>
Scheme] (let ((a 5) (b 2)) (+ a b))
7
Scheme] (+ a b)
unbound-variable
Scheme]
Alla variabile a viene assegnato il valore 5, a b viene assegnato il valore 2 poi
viene eseguita (+ a b), c’é da osservare che queste assegnazioni valgono solo per
questa istruzione infatti la successiva (+ a b) non ha successo.
1.1. Osservare che ’2=2
25
1.7 Le espressioni contenenti let
Supponiamo di voler calcolare l’espressione :
√
1
a2 + b2 + √
a2 + b2
√
con a=4 e b=3, siccome la sottoespressione a2 + b2 compare due volte possiamo porre:
Scheme] (let ((e (sqrt (+ (* 4 4) (* 3 3))))) (+ e (/ 1 e)))
5.2
Scheme]
E’ possibile ottenere un risultato analogo anche in questo modo:
Scheme] (let ((a 4) (b 3)) (let ((e (sqrt (+ (* a a) (* b b))))) (+
e (/ 1 e))))
5.2
Scheme]
Il secondo let va messo nella definizione del risultato del primo non nelle
assegnazioni.
L’espressione precedente é complicata, impariamo a scriverla correttamente:
corpo di let
I. (let () ())
zona assegnazioni
prima assegnazione
II. (let ((a 4)) ())
III. (let ((a 4) (b 3)) ())
seconda assegnazione
il secondo let va messo nel corpo del primo
IV. (let ((a 4) (b 3)) (let () ()))
solo nel corpo del primo let sono visibili le sue assegnazioni
nel corpo del primo let a=4 e b=3
V. (let ((a 4) (b 3)) (let (e (sqrt (+ (* a a) (* b b)))) (+ e (/ 1 e))))
nel corpo del secondo let e =
√
a2 + b2 .
26
Introduzione
Osserviamo attentamente il comportamento della seguente espressione:
Scheme] (let ((x 9)) (* x (let ((x (/ x 3))) (+ x x))))
54
Scheme]
Come funziona questa espressione? La variabile x é oggetto di due assegnazioni, una più esterna dove a x viene assegnato il numero 9 e una più interna
dove, essendo il secondo let nel corpo del primo, a x viene assegnato il valore
(/ 9 3), cioé 3, quindi é come se facessimo 9*6=54. Osservare che l’assegnazione
più interna oscura, nel corpo del relativo let, quella più esterna.
Consideriamo la seguente espressione (3a − b) + (3a + b) che come é evidente
coincide con 6a , in scheme si potrebbe scrivere nel seguente modo:
Scheme] (define a 3)
Scheme] (define b 2)
Scheme] (+ (- (* 3 a) b) (+ (* 3 a) b))
18
Scheme] ; l’espressione (* 3 a) viene valutata due volte
#<eof>
Scheme] (let ((a 3) (b 2)) (let ((a (* 3 a))) (+ (- a b) (+ a b))))
18
Scheme] ; con l’uso di let viene valutata solo una volta
#<eof>
Scheme] (let ((a (* 3 a))) (+ (- a b) (+ a b)))
18
Scheme] ; define ha ancora effetto su questa espressione, non e’
necessario porre (a 3) (b 2), cioe’ il primo let non era
necessario.
#<eof>
Scheme]
Esercizio 1.6. Riscrivi la seguente espressione:
Scheme] (cons (car (list ’a ’b ’c)) (cdr (list ’a ’b ’c)))
(a b c)
Scheme]
usando let e ricalcola l’espressione
Scheme] (let ((x (list ’a ’b ’c))) (cons (car x) (cdr x)))
1.8 Espressione lambda
27
(a b c)
Scheme]
1.8 Espressione lambda
Un’espressione lambda é, dal punto di vista sintattico, una cosa di questo tipo:
(lambda (var.1 var.2 .....) esp.1 esp.2 ......) , facciamo alcuni esempi:
Scheme] ((lambda (x) (* x x)) 2)
4
Scheme] ; un solo parametro e una sola espressione
#<eof>
Scheme] ((lambda (x y) (* x y) (+ x y)) 2 3)
5
Scheme] ; due parametri e due espressioni, apparentemente viene
considerata solo l’ultima ma non credo sia cosi’
#<eof>
Scheme] ((lambda (x y) (define a (* x y)) (+ x y)) 2 3)
5
Scheme] a
unbound-variable
Scheme] ; sembra proprio che non venga considerata , ma....
#<eof>
Scheme] ((lambda (x y) (define a (* x y)) (+ x y a)) 2 3)
11
Scheme] ;a viene vista solo nel corpo di lambda
Invece di usare define si può usare let, tenendo conto di tutte le regole che
riguardano let, ad esempio:
Scheme] (let ((a 3) (b 4)) ((lambda (x y) (sqrt (+ (* x x) (* y
y))))a b))
5.0
Scheme]
Vediamo come funziona:
28
Introduzione
assegnazioni di let
corpo di let
(let ( (a 3) (b 4) ) ( (lambda (x y) (sqrt (+ (* x x) (* y y)))) a b ))
parametri passati a lambda
parametri di lambda
(lambda ( x y ) ( sqrt (+ (* x x) (* y y)) ))
corpo di lambda
L’esempio seguente é veramente sconcertante e getta una nuova luce sul
valore che possono assumere i parametri di una espressione lambda.
Scheme] (define v_assoluto (lambda (x y) (sqrt (+ (* x x) (* y
y)))))
Scheme] (define operazione (lambda (simbolo op1 op2) (simbolo op1
op2)))
Scheme] (operazione + 3 2)
5
Scheme] (operazione v_assoluto 12 16)
20.0
Scheme] (operazione - (v_assoluto -8 -0) (operazione * 2 1))
6.0
Scheme] (operazione v_assoluto (* 36 3) (* 36 4))
180.0
Scheme] (operazione / (* 8 3) (* 8 2))
3/2
Scheme] (operazione + (operazione v_assoluto (* 36 3) (* 36 4))
(operazione - (v_assoluto -8 -0) (operazione * 2 1)))
186.0
Scheme]
I parametri di una espressione lambda possono essere di qualunque tipo,
anche il nome di un’altra espressione lambda.
Vogliamo ora introdurre un altro problema mediante il seguente esempio
Scheme] (define op_imp (lambda (x) (+x y)))
Scheme] (op-imp 3)
29
1.8 Espressione lambda
unbound-variable
Scheme] (define y 4)
Scheme] (op_imp 3)
unbound-variable
Scheme] (let ((y 4)) (op_imp 3))
unbound-variable
Scheme] (let ((y 4)) ((lambda (x) (+ x y)) 3))
7
Scheme] (define y 4)
Scheme] (define op_imp1 (lambda (x) (+x y)))
Scheme] (op-imp1 3)
unbound-variable
Scheme]
Come si vede dagli esempi precedenti non si possono usare delle funzioni
lambda che abbiano dei parametri non dichiarati nella apposita sezione e senza
assegnare loro un valore, per fare una cosa del genere bisogna inserire lambda
nel corpo di una espressione let e assegnare un valore al parametro non dichiarato nella apposita sezione dell’istruzione let. Sembra che solo let riesca a funzionare, ma vediamo qest’altro esempio:
Scheme] ((lambda (y) ((lambda (x) (+ x y)) 3)) 4)
7
Scheme]
anche un lambda più esterno può servire allo scopo. La variabile y, per il
lambda interno si chiama variabile libera , ma se vogliamo che l’espressione
possa essere valutata detta variabile libera deve essere legata ad un opportuno
valore o da una let più esterna o da una lambda più esterna.
Osserviamo questo altro interessantissimo esempio:
Scheme] (let ( (x ’a) )
assegnazione del primo let
corpo del primo let
(let ( (f (lambda (y) (list x y))) ) ( f ’b) ) )
assegnazione del secondo let
corpo secondo let
(a b)
Scheme]
Il secondo let si trova nel corpo del primo e quindi tutto ciò che é stato
legato nelle assegnazioni del primo let é visibile, infatti l’espressione funziona.
30
Introduzione
Modifichiamo l’esempio precedente nel seguente modo:
assegnazione secondo let
corpo secondo let
Scheme] (let ( (f (let ( (x ’a) ) ( lambda (y) (list x y) ))) )
assegnazione primo let
( f ’b ))
corpo primo let
(a b)
Scheme]
come si vede il risultato é sempre lo stesso.
Un’evoluzione della precedente espressione può essere la seguente:
Scheme] (let ((f (let ((x ’a)) (lambda (y) (list x y)))))
nel corpo del primo let
( let ( (x ’non) ) (f ’b)) )
ridefiniamo x, ma non succede nulla
(a b)
Scheme]
Il valore della variabile x rimane quello che aveva quando la funzione lambda
é stata costruita, anche se lo si varia, con un terzo let, prima di usare la
lambda , questa variazione viene ignorata.
L’espressione lambda ha una struttura di questo tipo
parametri racchiusi tra parentesi
(lambda ( parametro1 parametro2 ) (corpo))
Se si mette un solo parametro non racchiuso tra parentesi e poi alla funzione
si passa un numero qualsivoglia di parametri il comportamento sarà il seguente,
si forma una lista con i parametri passati e all’unico parametro dichiarato viene
passata questa lista, ad esempio:
unico parametro non racchiuso tra parentesi
Scheme] (define f (lambda x (cdr x)))
31
1.8 Espressione lambda
Scheme] (f 1 2 3 4 ’a ’(a m n))
vengono passati piu’ parametri
(2 3 4 a (a m n))
Scheme]
Un altro esempio
Scheme] (let ((f (lambda x (cons ’+ x)))) (f 2 3))
(+ 2 3)
Scheme] (+ 2 3)
5
Scheme]
Un ultimo modo di usare lambda é il seguente:
occhio al punto
(lambda (p1 p2 . pr) (corpo di lambda) val1 val2 val3
valn)
Le parentesi sono necessarie
in questo modo al parametro p1 viene attribuito il valore val1 , al parametro p2 viene attribuito il valore val2 , i valori restanti formano la lista
(val3 valn) che viene attribuita al parametro pr .
Esempio:
parametri
corpo
Scheme] ((lambda (p1 p2 . pr) (+ (* p1 p2) (+ (car pr) (car (cdr pr))))) 2 3 4
2
5 7 8)
3
’(4 5 7 8)
6
4
5
Scheme]
Il risultato dell’esempio sarà 15.
Una funzione lambda può quindi avere un numero predeterminato di parametri oppure averne un numero arbitrario.
Vediamo ulteriori esempi:
Scheme] ((lambda (p1 p2 . pr) (+ (* p1 p2) ((car pr) (car (cdr pr))
(car (cdr (cdr pr)))))) 2 4 + 5 6 ’a - 3)
19
32
Introduzione
Scheme]
Vediamo come funziona questo esempio
p2 ≡ 4
primo addendo
parametri
(car pr) ≡ +
((lambda (p1 p2 . pr)
(+ ( * p1 p2 ) ( (car pr) (car (cdr pr))
corpo
p1 ≡ 2
pr ≡ ’(+ 5 6 ’a - 3)
secondo addendo
(car (cdr (cdr pr))) )) ) 2 4 + 5 6 ’a - 3)
valori passati
1.9 Define
Quanto dichiarato nella zona assegnazioni di una istruzione let o nella zona
parametri di una istruzione lambda viene visto solo nel corpo di queste espressioni, ben diversamente si comporta define le cui assegnazioni vengono viste da
tutte le espressioni successive fine a quando non viene oscurata da qualche altra
assegnazione. Le associazioni stabilite da define vengono chiamate top-level ,
questa caratteristica fa si che define sia molto importante , infatti é una espressione che abbiamo già usato parecchie volte in precedenza, la sua sintassi é:
assumerà come valore: espressione
(define variabile espressione)
qualunque espressione valida
Vediamo alcuni esempi con define:
Scheme] ; quanto definito con define puo’ essere oscurato da let
oppure lambda
#<eof>
Scheme] (define addizione (+ 3 2))
Scheme] addizione
5
33
1.9 Define
Scheme] (let ((addizione ’(n o n - n u m e r o))) addizione)
(n o n - n u m e r o)
Scheme] ( (lambda (addizione) (+ addizione 2))
25)
27
Scheme] ; quanto definito con define puo’ essere oscurato solo nel
corpo di let o lambda ma successivamente a un let o un
lambda ritorna visibile
#<eof>
Scheme] (define a 3)
Scheme] ((lambda (a) (* a a)) 5 )
25
Scheme] (* a a)
9
Scheme]
Introduciamo due nuove funzioni che operano sulle liste:
Scheme] (define a ’(+ - + /))
Scheme] (cadr a)
Scheme] ;La funzione cadr restituisce il secondo elemento di una
lista
#<eof>
Scheme]
La funzione cadr é definita nel seguente modo:
Scheme] (define cadr (lambda (x) (car (cdr x))))
Scheme] (cadr a)
Scheme]
La seconda funzione che vogliamo introdurre é cddr
Scheme] (define lista (quote(! ? ^ % $ £)))
Scheme] (cddr lista)
(^ % $ £)
Scheme] ; cddr restituisce la lista alla quale sono stati tolti i
primi due elementi. La definizione di cddr e’ la seguente:
34
Introduzione
#<eof>
Scheme] (define cddr (lambda (y) (cdr (cdr y))))
Scheme] (cddr lista)
(^ % $ £)
Scheme] ; se la lista restituita e’ vuota ecco che cosa succede
#<eof>
Scheme] (cddr (cddr (cddr lista)))
()
Scheme]
Importante 1.1. Quando define é usato in combinazione con lambda la parola
lambda si può sopprimere, l’esatta sintassi da seguire dipende da come sono
definiti i parametri di lambda.
•
Se i parametri di lambda sono racchiusi tra parentesi e sono quindi in
numero predefinito si procede in questo modo:
(define nome (lambda (var1 var2 ...... varn) exp1 exp2 ...... ))
diventa
(define (nome var1 var2 ...... varn) exp1 exp2 ...... )
esempio:
Scheme] (define area (lambda (b h) (* b h)))
Scheme] (area 10 20)
200
Scheme] ;diventa
#<eof>
Scheme] (define (area b h)(* b h))
Scheme] (area 4 8)
32
Scheme]
•
Lambda ha un solo parametro non racchiuso tra parentesi
(define nome (lambda var exp1 exp2 ......))
diventa
(define (nome . var) exp1 exp2 ....)
Esempio:
Scheme] (define terza (lambda parola (car (cddr parola))))
Scheme] (terza 1 2 3 4 5)
3
35
1.9 Define
Scheme] ;diventa
#<eof>
Scheme] (define (terza . parola) (car (cddr parola)))
Scheme] (terza 1 2 3 4 5 6)
3
Scheme]
•
L’ultimo caso si ha quando lambda ha un parametro residuo, cioé é del
tipo:
parametro residuo
(lambda (p1 p2 ..... pn . pr) exp1 exp2 .......)
l’espressione define sarà quindi:
(define nome (lambda (p1 p2 ..... pn . pr) exp1 exp2 .......))
la quale si può abbreviare nel seguente modo:
(define (nome p1 p2 ......pn . pr) exp1 exp2 .......)
Esempio:
Scheme] (define operazione (lambda (p1 p2 . op) ((cadr op) p1
p2)))
Scheme] (operazione 6 3 + - * /)
3
Scheme] ;diventa
#<eof>
Scheme] (define (operazione p1 p2 . op) ((cadr op) p1 p2))
Scheme] (operazione 6 3 + - * /)
3
Scheme]
Queste abbreviazioni tendono a rendere il codice meno leggibile per cui sarà
opportuno farne un uso molto parco.
Consideriamo ora alcuni modi, molto interessanti, per usare define, ci sono
molte operazioni che hanno un solo parametro cioé che prendono un oggetto x
fanno qualche cosa e restituiscono un risultato, possiamo definire queste operazioni nel seguente modo:
Scheme] (define monadica (lambda (operazione)
(lambda (x) (operazione x x))))
Scheme] ;definiamo l’operazione monadica doppio
36
Introduzione
#<eof>
Scheme] (define doppio (monadica +))
Scheme] (doppio 2)
4
Scheme] (define quadrato (monadica *))
Scheme] (quadrato 7)
49
Scheme] (define coppia (monadica cons))
Scheme] (coppia ’a)
(a . a)
Scheme] ;definiamo una nuova operazione binaria
#<eof>
Scheme] (define modulo_vettore (lambda (x y) (sqrt (+ (* x x) (* y
y)))))
Scheme] (define mod_vett_45 (monadica modulo_vettore))
Scheme] (mod_vett_45 10)
14.142135623731
Scheme] (* (mod_vett_45 10) (mod_vett_45 10))
200.0
Scheme] ;osservare questa espressione
#<eof>
Scheme] (define monadica_tutto (lambda (op x) ((monadica op) x)))
Scheme] (monadica_tutto modulo_vettore 10)
14.142135623731
Scheme] (monadica_tutto + 6)
12
Scheme]
In define una procedura può anche usare il nome di una procedura non ancora
definita, esempio:
Scheme] (define es_a (lambda (x y) (es_b x y)))
Scheme] ;come si vede tutto e’ andato bene e non e’ stato
evidenziato il fatto che es_b non e’ definita, ma se
proviamo a usare es_a ecco che cosa succede
#<eof>
Scheme] (es_a 8 7)
unbound-variable
1.9 Define
37
Scheme] ;viene evidenziato il fatto che esiste una variabile
sconosciuta, ma se definiamo es_b succede che:
#<eof>
Scheme] (define es_b *)
Scheme] ;ripetiamo quanto fatto prima
#<eof>
Scheme] (es_a 8 7)
56
Scheme]
Osservazione 1.2. Le istruzioni car e cdr hanno alcune varianti che illustriamo
con i seguenti esempi:
Scheme] (define str (quote(((1 2) 3) a b c d e f g h i l m n)))
Scheme] (car str)
((1 2) 3)
Scheme] (caar str)
(1 2)
Scheme] (caaar str)
1
Scheme] (caaaar str)
wrong-type-arg
Scheme] (cdr str)
(a b c d e f g h i l m n)
Scheme] (cddr str)
(b c d e f g h i l m n)
Scheme] (cdddr str)
(c d e f g h i l m n)
Scheme] (cddddr str)
(d e f g h i l m n)
Scheme] (cdddddr str)
unbound-variable
Scheme] ;arriva fino a 5 d
#<eof>
Scheme] ; le due cose si possono combinare assieme
#<eof>
38
Introduzione
Scheme] (cadr str)
a
Scheme] (caddr str)
b
Scheme] (cdar str)
(3)
Scheme] (cdaar str)
(2)
Scheme] (cdaaar str)
wrong-type-arg
Scheme]
Vediamo nel dettaglio il funzionamento di uno di questi esempi
1 - il primo a fa il car di str e restituisce ((1 2) 3)
2 - il secondo a fa il car del 1 passo e restituisce (1 2)
cdaar str
3 - il primo d fa il cdr del 2 passo e restituisce (2)
1.10 Espressioni con condizione
Cominciamo con l’istruzione if la sua forma é di questo tipo:
if test espressione_test_vero espressione_test_falso
Vediamo alcuni esempi:
Scheme] (if (< 3 2) (* 3 2) (+ 3 2))
5
Scheme] (if (=
3 3) (quote(vero)) (quote(falso)))
(vero)
Scheme] (if (=
3 6) (quote(vero)) (quote(falso)))
(falso)
Scheme] ; esempio complicato
1.10 Espressioni con condizione
39
#<eof>
Scheme] (define calcolo_radici (lambda (a b c) (if (>= (- (* b b) (*
4 a c)) 0) (quote(ci sono radici)) (quote(non ci sono
radici)))))
Scheme] (calcolo_radici 1 3 2)
(ci sono radici)
Scheme] (calcolo_radici 10 2 -9)
(ci sono radici)
Scheme] (calcolo_radici 100 -5 6)
(non ci sono radici)
Scheme]
Con if si possono usare tutte le possibili espressioni booleane ma anche le
altre, ad esempio:
Scheme] (if (not(= 3 3)) (quote(vero)) (quote(falso)))
(falso)
Scheme] (if (<= (+ 8 2) (* 5 2)) ’(vero) ’(falso))
(vero)
Scheme] (define a 3)
Scheme] (define b 5)
Scheme] (if (> a b) ’(vero) ’(falso))
(falso)
Scheme] (if (+ a b) ’(vero) ’(falso))
(vero)
Scheme] (if (/ a 0) ’(vero) ’(falso))
numerical-overflow
Scheme] (if (/ 0 a) ’(vero) ’(falso))
(vero)
Scheme] (if (sqrt (* -1 a)) ’(vero) ’(falso))
(vero)
Scheme] (if (define c 16) ’(vero) ’(falso))
(vero)
Scheme] (sqrt c)
4.0
Scheme] (sqrt (* -1 c))
40
Introduzione
0.0+4.0i
Scheme]
Si possono anche usare le operazioni booleane and e or
Scheme] (if (or (< 5 4) (= 0 (- 5 5))) ’(vero) ’(falso))
(vero)
Scheme] (if (and (+ 4 5) (< 3 28) (= 6 (+ 3 3))) "vero" "falso")
"vero"
Scheme]
Una espressione booleana si può anche testare direttamente:
Scheme] (< 8 2)
#f
Scheme] ;#f vuol dire
false
#<eof>
Scheme] (= (* 6 7) 42)
#t
Scheme] ;#t significa
true
#<eof>
Scheme] (or #t #f #t)
#t
Scheme] (and #t #f #t)
#f
Scheme] (or #f (and #t #t) #f)
#t
Scheme]
Molto interessante é l’uso del punto interrogativo
Scheme] (null? (- 5 5))
#f
Scheme] ;null non significa nullo ma insieme vuoto
#<eof>
Scheme] (null? (cdr(quote(a))))
#t
Scheme] (symbol? *)
41
1.10 Espressioni con condizione
#f
Scheme] (integer? 3)
#t
Scheme] (real? 6)
#t
Scheme] (complex? (sqrt -4))
#t
Scheme] (procedure? *)
#t
Scheme] (define quadrato (lambda (x) (* x x)))
Scheme] (procedure? quadrato)
#t
Scheme] (procedure? quadrato1)
unbound-variable
Scheme]
Questa roba può essere utile per scrivere del codice robusto come in questo
esempio
Scheme] (define piu (lambda (x y) (if (and (number? x) (number? y))
(+ x y) "fai attenzione pirla")))
Scheme] (piu 3 5)
8
Scheme] (piu ’a ’g )
"fai attenzione pirla"
Scheme] (piu 3 =)
"fai attenzione pirla"
Scheme]
Un’altra espressione simile a if é cond
, la sua sintassi é la seguente:
(cond (test1 esp1) (test2 exp2.) .......... (testn expn.) (else exp.))
else si può anche omettere
Vediamo un esempio con cond:
42
Introduzione
Scheme] (define controllo (lambda (x) (cond ((>= x 100) "x>100")
((and (> x 0) (< x 100)) "x compreso tra 10 e 100") (else
"e’ da un’altra parte"))))
Scheme] (controllo 123)
"x>100"
Scheme] (controllo 56)
"x compreso tra 10 e 100"
Scheme] (controllo -125)
"e’ da un’altra parte"
Scheme]
1.10.1 Esercizi
Esercizio 1.7. Definisci il predicato atomo? che restituisce vero se il suo unico argomento
non é una coppia falso se invece é una coppia.
Soluzione.
Scheme] (define atomo? (lambda (arg) (if (not(pair? arg)) "vero"
"falso")))
Scheme] (atomo? 5)
"vero"
Scheme] (atomo? ’(a.b))
"falso"
Scheme] (atomo? ’(a b))
"falso"
Scheme] (atomo? ’(a b c))
"falso"
Scheme] (atomo? +)
"vero"
Scheme] (atomo? ’(12 . 4))
"falso"
Scheme] (atomo? (+ 3 2))
"vero"
Scheme] (pair? ’(a b c d e f))
#t
Scheme] ; pair? ha un funzionamento un poco strano
#<eof>
1.11 Ricorsione
43
Scheme] (atomo? (a b))
unbound-variable
Scheme]
Esercizio 1.8. La procedura length accetta come parametro una lista e ne restituisce la
sua lunghezza. Scrivere una procedura ListaCorta che accetta come parametri due liste e
restituisce la più corta.
Soluzione.
Scheme] (define ListaCorta (lambda (l1 l2) (if (<= (length l1)
(length l2)) l1 l2)))
Scheme] (ListaCorta ’(a b) ’(1 2 3 4))
(a b)
Scheme] (ListaCorta ’(a b c d) (cdr ’(a b c d)))
(b c d)
Scheme]
1.11 Ricorsione
Consideriamo le seguenti righe di codice:
Scheme] (define pot
(lambda (base esp)
(if (= esp 1) base
(* (pot base (- esp 1)) base))))
Scheme] (pot 2 1)
2
Scheme] (pot 2 2)
4
Scheme] (pot 2 3)
8
Scheme] (pot 2 4)
16
Scheme] (pot 2 5)
32
Scheme] (pot 2 6)
64
44
Introduzione
Scheme] (pot 2 7)
128
Scheme] (pot 15 5)
759375
Scheme]
Vediamo di capire come funzione la procedura pot precedentemente definita:
Scheme] (define pot
(lambda (base esp)
passo base , quando viene eseguita questa
(if (= esp 1) base parte la ricorsione finisce
passo ricorsivo
(* (pot base (- esp 1)) base))))
viene richiamata pot con esp diminuito di 1
La procedura deve contenere al suo interno due parti: il passo base e il
passo ricorsivo , il passo base é quella parte del codice che termina la ricorsione, infatti quando esp diventa 1 pot assume il valore della base e non viene
richiamata pot stessa; poi vi é il passo ricorsivo , in questa fase viene richiamata pot, infatti pot assume il valore dato dal prodotto di pot, con la stessa
base e esponente diminuito di 1, per la base stessa.
Sicuramente si arriva a esp=1 perché lo si diminuisce sempre di 1, nel passo
ricorsivo, quindi la ricorsione ha sicuramente termine.
Vediamo come funziona ad esempio (pot 2 4)
| (pot 2 4)
richiamo di 24
|| (pot 2 3)
richiamo di 23
||| (pot 2 2)
richiamo di 22
|||| (pot 2 1)
richiamo e calcolo di 21
2
|||
4
calcolo di 22
||
8
calcolo di 23
16
calcolo di 24
|
45
1.11 Ricorsione
In matematica ci sono parecchie definizioni date in modo ricorsivo una di
queste è la definizione di fattoriale di un numero: n! = (n − 1)! · n, se n=1 1!=1
vediamo come tradurre questa definizione in una procedura ricorsiva:
Scheme] (define fatt (lambda (n) ;parametro
(if (= n 1) 1 ;corpo di lambda - passo base
(* (fatt (- n 1)) n)))); passo ricorsivo
Scheme] (fatt 2)
2
Scheme] (fatt 6)
720
Scheme] (fatt 50)
30414093201713378043612608166064768844377641568960512000000000000
Scheme]
Esempio 1.3. Scrivere una procedura che presa una lista e un oggetto restituisca la sottolista, se esiste, che inizia con l’oggetto passato e va fino alla fine
della lista, altrimenti restituisce falso.
Scheme] (define sottolista (
lambda (og ls)
(cond ((null? ls) #f ) ((eqv? (car ls) og) ls )
(else (sottolista og (cdr ls))))))
Scheme] (sottolista ’c ’(a b c d e))
(c d e)
Scheme] (sottolista ’x ’(a b c d e f))
#f
Scheme]
Osservazione 1.4. L’esempio precedente é piuttosto interessante perché
sono due passi base
ci
Scheme] (define sottolista (
lambda (og ls)
10 passo base
(cond ((null? ls) #f ) ((eqv? (car ls) og) ls )
passo ricorsivo
2o passo base
(else (sottolista og (cdr ls))))))
46
Introduzione
Scheme] (sottolista ’c ’(a b c d e))
(c d e)
Scheme] (sottolista ’x ’(a b c d e f))
#f
Scheme]
Esempio 1.5. Scrivere una procedura avente due parametri, un oggetto e una
lista di quel tipo di oggetti e cancellare dalla lista tutte le occorrenze
dell’oggetto e restituire la nuova lista.
Scheme] (define lista-epurata
(lambda (og ls)
(cond ((null? ls) ’())
((eqv? (car ls) og) (lista-epurata og (cdr ls)))
(else (cons (car ls) (lista-epurata og (cdr
ls)))))))
Scheme] (lista-epurata ’a ’(a y z a))
(y z)
Scheme] (lista-epurata ’h ’(q w e s))
(q w e s)
Scheme] (lista-epurata ’a ’(a a a a))
()
Scheme] (lista-epurata 3 2)
wrong-type-arg
Questo esempio é interessante perché ha due passi ricorsivi
Scheme] (define lista-epurata
(lambda (og ls)
(cond ((null? ls) ’())
10 passo ricorsivo
passo base
((eqv? (car ls) og) (lista-epurata og (cdr
20 passo ricorsivo
ls)))
(else (cons (car ls) (lista-epurata og (cdr
ls)))))))
Vediamo ora una nuova procedura, la procedura map , per introdurla consideriamo il seguente esempio, supponiamo di avere una lista di numeri e di voler
ottenere la lista dei loro doppi, potremmo procedere con la ricorsione come
segue:
1.11 Ricorsione
47
Scheme] (define lista-doppia (lambda (ingr)
(if (null? ingr) ’()
(cons (* 2 (car ingr))
(lista-doppia(cdr ingr))))))
Scheme] (lista-doppia ’(2 3 4 5))
(4 6 8 10)
Scheme]
In pratica su ogni elemento della lista viene fatta la stessa operazione,
Scheme ha una funzione apposta per fare questo ed é map:
Scheme] (map (lambda (x) (* 2 x)) ’(2 3 4 5))
(4 6 8 10)
Scheme]
Dopo map bisogna mettere l’operazione da fare su ogni elemento della lista e
poi la lista.
Scheme] (map abs ’(-1 2 -8 3 -4 -9))
(1 2 8 3 4 9)
Scheme] (map cdr ’((a b) (s h) (3 5 9)))
((b) (h) (5 9))
Scheme]
Osserviamo questa interessante applicazione di map
Scheme] (map * ’(1 3 2 5) ’(4 5 6 9))
(4 15 12 45)
Scheme]
Se passiamo a map due liste della stessa lunghezza e indichiamo cosa fare tra
gli elementi dello stesso posto delle due liste verrà restituita una lista costituita
dai risultati di questa operazione.
Scheme] (map (lambda (x y) (+ 1 (* x y))) ’(3 4 5) ’(1 2 3))
(4 9 16)
Scheme] (map cons ’((a b) c (1 2 3)) ’((1 2) s (4 5)))
(((a b) 1 2) (c . s) ((1 2 3) 4 5))
Scheme]
48
Introduzione
Esercizio 1.9. Scrivere una procedura che fonda due liste in una sola cioé data ad esempio
la lista ’(a b c) e la lista ’(1 2 3) restituisca la lista ’(a b c 1 2 3)
Scheme] (define fusione (lambda (ls1 ls2)
(if (null? ls1) ls2
(cons (car ls1) (fusione (cdr ls1) ls2)))))
Scheme] (fusione ’(a b c) ’(1 2 3))
(a b c 1 2 3)
Scheme]
Esercizio 1.10. Scrivere una funzione che prende in ingresso un numero n intero positivo
non nullo e un qualsiasi oggetto e restituisce in uscita una lista contenente n oggetti tutti
uguali all’oggetto passata in ingresso.
Scheme] (define lstn (lambda (n og)
(if (= n 0) ’()
(cons og (lstn (- n 1) og)))))
Scheme] (lstn 4 ’a)
(a a a a)
Scheme] (lstn 5 3)
(3 3 3 3 3)
Scheme] (lstn 3 ’(+))
((+) (+) (+))
Scheme]
Esercizio 1.11. Scrivere una funzione che prenda in ingresso due liste e restituisca la più
corta.
Scheme] (define lung (lambda (l) (if (null? l) 0
(+ 1 (lung (cdr l))))))
Scheme] (lung ’(a s d))
3
Scheme] ; dopo aver ridefinito la funzione lenght possiamo utilizzarla come
gia’ fatto in precedenza
#<eof>
Scheme] (define corta (lambda (l1 l2)
(if (< (lung l1) (lung l2)) l1 l2)))
Scheme] (corta ’(1 2 3 3 4) ’(a s d))
(a s d)
Scheme] (corta ’() ’(1 2 3))
()
Scheme] (corta ’(m n b) ’(1 2 3))
(1 2 3)
Scheme]
1.12 Assegnamento
49
Esercizio 1.12. Scrivere due funzioni una che richiama l’altra realizzando così una ricorsione indiretta.
Scheme] (define a (lambda (n)
(if (= n 0) 0 (* n (b (- n 1))))))
Scheme] (define b (lambda (m)
(if (= m 0) 1 (+ m (a (- m 1))))))
Scheme] (a 3)
9
Scheme] (b 4)
13
Scheme] (b 5)
25
Scheme] (a 8)
1256
Scheme]
Esercizio 1.13. Scrivere una funzione che prenda una lista di coppie, come ad esempio
((a . 1) (b . 2) (c . 3)), e restituisca una coppia di liste , ad esempio ((a b c) (1 2 3))
Scheme] (define duo (lambda (lc)
(cons (map car lc)
(list(map cdr lc)))))
Scheme] (duo ’((a . 1) (b . 2) (c . 3)))
((a b c) (1 2 3))
Scheme]
1.12 Assegnamento
La funzione di assegnamento serve a ridefinire una variabile alla quale é già
stato assegnato un valore tramite qualche altra funzione, l’istruzione che la rappresenta é set! , vediamo alcuni esempi:
Scheme] (define a 5)
Scheme] a
5
Scheme] (set! a ’(a b c))
Scheme] a
(a b c)
Scheme] ;cambia il valore che la variabile a aveva assunto grazie a
define
#<eof>
50
Introduzione
Scheme] (set! b 7)
unbound-variable
Scheme] ; non si puo’ usare set! su una variabile non ancora
definita
#<eof>
Scheme] (define g (lambda (x) (set! a x)))
Scheme] a
(a b c)
Scheme] (g 9)
Scheme] a
9
Scheme] ;la funzione g ha come unico effetto quello di cambiare il
valore della variabile a
#<eof>
Scheme] (let ((a1 2) (a2 4)) (set! a1 10) (set! a2 20) (* a1 a2))
200
Scheme]
Sul nostro testo di riferimento [1, a pag. 38] c’é un esempio svolto che
riguarda la soluzione delle equazioni di 2ř grado, é molto interessante e quindi
proviamo a risolvere una equazione di 2ř grado:
Scheme] (define equ-secondo (lambda (a b c)
(let ((meno_b 0) (delta 0) (rad_delta 0) (den 0) (x1 0) (x2
0))
(set! meno_b (- 0 b))
(set! delta (- (* b b) (* 4 a c)))
(set! rad_delta (sqrt delta))
(set! den (* 2 a))
(set! x1 (/ (+ meno_b rad_delta) den))
(set! x2 (/ (- meno_b rad_delta) den))
(cons x1 x2))))
Scheme] (equ-secondo 1 -5 6)
(3.0 . 2.0)
Scheme] (equ-secondo 1 5 6)
(-2.0 . -3.0)
Scheme] (equ-secondo 1 2 5)
(-1.0+2.0i . -1.0-2.0i)
Scheme] ;usa con molta disinvoltura i numeri complessi
1.12 Assegnamento
51
#<eof>
Scheme]
Sempre sullo stesso testo [1, a pag. 38 ] c’é tutta una lamentela sull’uso di
set! , e , se ho capito bene (é tutto scritto in inglese) insiste sull’inutilità
dell’uso di set!, anche se ammette che può rendere più leggibile il codice, e rifà
l’esercizio precedente senza usare set!, proviamoci anche noi.
Scheme] (define equ2 (lambda (a b c)
(let ((mb (- 0 b))
(rd (sqrt (- (* b b) (* 4 a c))))
(dv (* 2 a)))
(cons (/ (+ mb rd) dv)
(/ (- mb rd) dv)))))
Scheme] (equ2 1 -5 6)
(3.0 . 2.0)
Scheme] (equ2 1 -6 9)
(3.0 . 3.0)
Scheme] (equ2 3 8 4)
(-0.666666666666667 . -2.0)
Scheme] (equ2 8 1 20)
(-0.0625+1.57990308247057i . -0.0625-1.57990308247057i)
Scheme] ;fatto
#<eof>
Scheme]
Sempre sul solito testo [1, a pag. 39 ] c’é un interessante metodo per modificare il comportamento di cons facendo si che ogni volta che viene usata cons
non solo si ottenga il solito risultato ma venga incrementato un apposito contatore che in ogni istante ci dice quante volte cons é stata usata, proviamo anche
noi a fare una cosa del genere.
Scheme] (define c 0)
Scheme] c
0
Scheme] (set! cons (let ((vcons cons))
(lambda (x y) (set! c (+ c 1)) (vcons x y))))
Scheme] (cons 3 ’(g h j))
(3 g h j)
52
Introduzione
Scheme] c
2692
Scheme] (cons ’(a s) ’(4 5))
((a s) 4 5)
Scheme] c
5379
Scheme] (cons 1 ’(5))
(1 5)
Scheme] c
9377
Scheme]
Come si vede c assume valori del tutto inspegabili, c’é qualche cosa che
non va, provo a rifare la stessa cosa su un altro sistema DrRacket .
Lanciato DrRacket bisogna seguire le istruzioni riportate in http://tedgao.blogspot.it/2011/08/learning-scheme-using-drracket.html , in particolare
quanto riportato nella figura sottostante:
1.12 Assegnamento
53
e, dopo aver premuto Run , nella sezione più bassa riportare quanto già
fatto nella sezione Scheme , ecco che cosa succede
Non si può ridefinire cons , proviamo a procedere in quest’altro modo che
forse funziona: 1.2
Scheme] (define c 0)
Scheme] c
0
Scheme] (define mcons cons)
Scheme] (set! mcons (lambda (x y) (set! c (+ c 1)) (cons x y)))
Scheme] c
0
Scheme] (mcons 3 ’(g h j))
(3 g h j)
Scheme] c
1
Scheme] (mcons ’(a s) ’(4 5))
((a s) 4 5)
Scheme] c
2
1.2. Ho impiegato due giorni per risolvere il problema ed ho pure dovuto reinstallare il sistema operativo.
54
Introduzione
Scheme] (mcons ’a (mcons ’b ’(1 2 3)))
(a b 1 2 3)
Scheme] c
4
Scheme] ;tutto funziona come deve funzionare.
Set! pare sia molto utilizzata per memorizzare lo stato interno di una funzione, ad esempio proviamo a scrivere una funzione che conta quante volte é già
stata chiamata, ci deve essere al suo interno una variabile che si aggiorna tutte
le volte che la funzione viene chiamata (vedi [1, pag. 40])
Scheme] (define contatore 0)
Scheme] (define conta (lambda () (set! contatore (+ contatore 1))
contatore))
Scheme] (conta)
1
Scheme] (conta)
2
Scheme] (conta)
3
Scheme] ;se rivalutiamo la prima linea (define contatore 0) ecco che
cosa succede
#<eof>
Scheme] (conta)
1
Scheme] ; il contatore viene di nuovo azzerato
Il fatto che il contatore venga inizializzato in una apposita linea di codice
non é una bella cosa, proviamo in questo altro modo:
Scheme] (define conta (let ((contatore 0))
(lambda () (set! contatore (+ contatore 1))
contatore)))
Scheme] (conta)
1
Scheme] (conta)
2
Scheme] (+ (conta) 4)
7
1.12 Assegnamento
55
Scheme] ; etc........
Osservazione 1.6. Vediamo ora di costruire una procedura che crea tutta una
serie di contatori uno indipendente dall’altro.
Scheme] (define crea (lambda ()
(let ((contatore 0))
(lambda () (set! contatore (+ contatore 1))
contatore))))
Scheme] (define cnt1 (crea))
Scheme] (define cnt2 (crea))
Scheme] (cnt1)
1
Scheme] (cnt1)
2
Scheme] (cnt2)
1
Scheme] (+ 6 (cnt2))
8
Scheme] (cnt2)
3
Scheme] (cnt2)
4
Scheme] (cnt1)
3
Scheme]
C’é molta perplessità sul funzionamento di define, let , lambda, set! etc. evidentemente non tutto é stato spiegato. La perplessità nasce dal comportamento
di set!, proviamo con questo esempio:
Scheme] (define pp (let ((a 2) (b 4))
(set! a (+ a 2)) (set! b (+ b 1)) (+ a b)))
Scheme] (pp)
misc-error
Scheme] pp
9
Scheme] pp
9
56
Introduzione
Scheme] ;in questo esempio set! sembra non aver avuto effetto,
proviamo a mettere il corpo di let in una lambda
#<eof>
Scheme] (define pp1 (let ((a 2) (b 4))
(lambda ()
(set! a (+ a 2)) (set! b (+ b 1)) (+ a b))))
Scheme] pp1
#<procedure pp1 ()>
Scheme] ; osservare il risultato
#<eof>
Scheme] (pp1)
9
Scheme] (pp1)
12
Scheme] (pp1)
15
Scheme] ; con lambda set! funziona
Ci sono alcune cose da osservare, nel primo dei due esempi precedenti “define
pp” tratta pp come se fosse un valore, infatti per richiamarlo si usa semplicemente pp e non (pp), nel secondo esempio “define pp1” a pp1 viene associata la
lambda , il let sembra quasi ignorato funziona solo la lambda, tranne che la
prima volta, e per richiamarlo bisogna usare le parentesi (pp1). L’impressione
che ho avuto é che in define il let venga valutato solo al momento della sua
scrittura e poi in pp rimanga sempre lo stesso valore, mentre lambda viene valutato tutte le volte che si richiama la funzione, tutto questo spiega perchè set!
funziona solo dentro un lambda e non dentro let.
Studiamo adesso il seguente esempio:
Scheme] (define pigro1 (lambda (t)
(let ((val #f) (flag #f))
(lambda ()
(if (not flag)
(begin (set! val (t))
(set! flag #t))
val)))))
Scheme] (define pr1 (pigro1
(lambda ()
(display "Solo per questa volta")
(newline) "questo sempre")))
Scheme] (pr1)
Scheme] (pr1)
1.12 Assegnamento
57
"questo sempre"
Scheme] (pr1)
"questo sempre"
Scheme] (display "Solo per questa volta")
Scheme] ; e’ chiaro che la procedura display con Texmacs non
funziona, proviamo con DrRacket
#<eof>
Scheme]
Anche DrRaket , se usa r6rs , non funziona
Non riconosce display come procedura, proviamo a non usare r6rs ma proprio racket.
Proviamo le due procedure precedenti con racket
58
Introduzione
Scheme] (define pigro1 (lambda (t)
(let ((val #f) (flag #f))
(lambda ()
(if (not flag)
(begin (set! val (t))
(set! flag #t))
val)))))
Scheme] (define pr1 (pigro1
(lambda ()
(display "Solo per questa volta")
(newline)
"questo sempre")))
Scheme]
Facciamo funzionare il tutto
Funziona tutto come deve funzionare, vediamo di spiegare il perché di questo
comportamento.
59
1.12 Assegnamento
Scheme] (define pr1 (pigro1
(lambda ()
Stampa la frase sullo schermo
(display "Solo per questa volta")
(newline)
Valore restituito da pr1
"questo sempre")))
La procedura pr1 stampa sullo schermo Solo per questa volta , senza virgolette, e successivamente posiziona il cursore all’inizio di una nuova riga, infine
restituisce la frase “questo sempré’ , il fatto che restituisca questo valore non
vuol dire che lo stamperà sullo schermo, in particolare se verrà chiamata entro
un’altra procedura non verrà stampato.
Vediamo ora come funziona l’altra procedura la pigro1.
(define pigro1 (lambda (t)
(let ((val #f) (flag #f))
(lambda ()
(if (not flag)
(begin (set! val (t))
Blocco if
vero
(set! flag #t))
val)))))
falso
La procedura pigro1 é un lambda con un parametro t e un corpo che é una
let che fissa due parametri booleani, val e flag, a falso ed ha un corpo che é
un’altra lambda la quale non ha parametri ed ha un corpo che é una if. La
pigro1 viene chiamata da pr1 che gli passa la procedura t:
Scheme] (lambda ()
(display "Solo per questa volta")
(newline)
"questo sempre")
La prima volta che pigro1 viene chiamata, non essendo ancora intervenuta
set!, ha i due parametri della let, val e flag, settati a falso dalla let stessa, pertanto dell’if viene eseguita la parte “vero” e quindi le due linee del begin, viene
valutata
(t),
in
particolare
display
stamperà
sullo
schermo
, la lambda poi restituisce “questo sempré’ e val verrà
Solo per questa volta
settata a questo valore dalla set! mentre flag verràs settato a vero dalla successiva set!, la parte falso non viene eseguita. Le volte successive pigro1 ha flag settata a vero e val settata a “questo sempré’ quindi dell’if viene eseguita la parte
60
Introduzione
falso cioé viene restituito “questo sempré’ che essendo il risultato della funzione
chiamata verrà stampato sullo schermo.
In Texmacs la funzione display non viene valutata e questo spiega il funzionamento anomalo visto in precedenza.
Adesso, sempre per la serie “vediamo come si usa set!”, costruiamo un
costruttore di stack. Lo stack è una pila di oggetti che vengono inseriti e estratti
sempre dalla stessa parte:
Lo stack é un oggetto contenente una lista che rappresenta la pila dello stack
e inoltre accetta 4 tipi di messaggi:
1. ’vuoto e l’oggetto risponde con #t se la pila é vuota altrimenti risponde
#f
2. ’push allora aggiunge in testa alla pila un oggetto che verrà passato come
argomento,
3. ’top restituisce l’elemento che sta in cima alla pila
4. ’pop cancella l’elemento che sta in cima alla pila
Scheme] (define fabb-stack (lambda ()
(let ((pila ’()))
(lambda (mess . arg)
(cond
((eqv? mess ’vuoto) (null? pila))
((eqv? mess ’push)
(set! pila (cons (car arg) pila)))
((eqv? mess ’pop)
(set! pila (cdr pila)))
((eqv? mess ’top)
(car pila))
(else "va a ...."))))))
1.12 Assegnamento
61
Scheme] (define stack1 (fabb-stack))
Scheme] (stack1 ’vuoto)
#t
Scheme] (stack1 ’push ’a)
Scheme] (stack1 ’vuoto)
#f
Scheme]
Scheme]
Scheme]
Scheme]
(define
(stack2
(stack2
(stack2
stack2 (fabb-stack))
’push ’ab)
’push ’wacb)
’top)
wacb
Scheme] (stack1 ’top)
a
Scheme] (stack1 ’push ’gh)
Scheme] (stack1 ’push ’avb)
Scheme] (stack1 ’top)
avb
Scheme] (stack1 ’pop)
Scheme] (stack1 ’top)
gh
Scheme] (stack2 ’top)
wacb
Scheme] (stack1 ’pop)
Scheme] (stack1 ’top)
a
Scheme] (stack2 ’top)
wacb
Scheme] (stack2 ’pop)
Scheme] (stack2 ’top)
ab
Scheme]
Molto bene tutto funziona come deve funzionare, questa procedura assomiglia
molto alla procedura vista in 1.6 a pag. 55 anche lì c’era una lambda esterna
priva di parametri, il corpo di detta lambda costituito da una let con un parametro che inizializza la variabile più importante , una lambda nel corpo della let
che definisce la funzione da creare , in entrambi i casi set! serve a tenere traccia
del funzionamento della funzione.
62
Introduzione
Indice analitico
accento ’
.. . .
and . . . . . . . .
car . . . . . . . .
cdr . . . . . . . .
commento ; . . .
cond . . . . . . . .
cons . . . . . . . .
coppia . . . . . . .
define . . . . . . .
DrRacket . . . . .
espressione . . . .
espressione lambda
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
14
40
15
15
10
41
15
16
11
52
10
27
if . . . . . . .
let . . . . . . .
or . . . . . . .
passo base . .
passo ricorsivo
procedure . . .
quote . . . . .
simbolo . . . .
stringhe . . . .
top-level . . .
variabile libera
63
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
38
24
40
44
44
11
14
16
11
32
29
Bibliografia
[1] R. Kent Dybvig. The Scheme programming language, volume Unico. MIT, 2 edition.
65
Scarica

Scheme - Torrero Giovanni, Giovanni Torrero