Abbiamo fatto pratica con le variabili e le funzioni, ora si entra nella palude fangosa delle liste di Scheme.
Prima di approfondire le liste è necessario comprendere la differenza tra valori atomici e liste.
Avete già visto i valori atomici quando abbiamo inizializzato le variabili nella sessione precedente. Un valore atomico è un valore singolo. Ad esempio possiamo assegnare alla variabile "x" il valore 8 nell'istruzione seguente:
(let* ( (x 8) ) x)
(Abbiamo aggiunto l'espressione x
alla fine per
stampare il valore assegnato a x
-- normalmente non
si dovrebbe averne bisogno. Si noti come let*
operi come
una funzione: il valore dell'ultima istruzione è il valore
restituito.)
Una variabile può anche riferirsi ad una lista di valori piuttosto che a
un singolo valore. Per assegnare alla variabile x
la
lista di valori 1, 3, 5 si digiti:
(let* ( (x '(1 3 5))) x)
Si provi a digitare entrambe le istruzioni nella console Script-fu e si osservino le risposte. Quando si digita la prima istruzione si ottiene semplicmente il risultato:
8
Mentre se si digita l'altra istruzione si ottiene il seguente risultato:
(1 3 5)
Quando si ottiene il valore 8 l'interprete sta informando che
x
contiene il valore atomico 8 mentre quando si
ottiene (1 3 5) sta informando che x
contiene non
un valore singolo bensì una lista di valori. Si noti che non ci sono
virgole nella dichiarazione o assegnamento della lista, tantomeno nel
risultato stampato.
La sintassi per definire una lista è:
'(a b c)
dove a
, b
, e
c
sono letterali. Si usa l'apostrofo (') per
indicare che ciò che segue nelle parentesi è una lista di valori
letterali piuttosto che una funzione o un'espressione.
Una lista vuota può essere definita come segue:
'()
o semplicemente:
()
Le liste possono contenere valori atomici così come altre liste:
(let* ( (x '("GIMP" (1 2 3) ("is" ("great" () ) ) ) ) ) x )
Si noti che dopo il primo spostrofo non vi è più bisogno di utilizzare un apostrofo per definire le liste interne. Si provi a copiare l'istruzione nella console Script-Fu e ad eseguirla per vedere cosa restituisce.
Si noti come il risultato restituito non è una lista di valori atomici
singoli ma piuttosto è una lista di letterali
("GIMP")
, la lista (1 2 3)
, ecc.
E' utile pensare alle liste come composte di una «testa» e una «coda». La testa è l'elemento iniziale della lista, la coda è la parte restante. Si capirà l'importanza di questo concetto quando si parlerà di come si compongono le liste e come accedere agli elementi di una lista.
Una delle funzioni più comuni che si incontrano è la funzione cons. Prende un valore e lo mette in testa al suo secondo argomento, una lista. Nel capitolo precedente si è suggerito di pensare una lista come composta da un elemento (la testa) è la parte restante (la coda), questo è esattamente il comportamento della funzione cons -- aggiunge un elemento in testa alla lista.Si potrebbe creare una lista come segue:
(cons 1 '(2 3 4) )
Il risultato è la lista (1 2 3 4)
.
Si può anche creare una lista con un solo elemento:
(cons 1 () )
Si possono utilizzare variabili dichiarate in precedenza al posto di qualunque letterale come ci si aspetta.
Per definire una lista composta da letterali oppure da variabili precedentemente dichiarate si utilizza la funzione list:
(list 5 4 3 a b c)
Ciò costruirà e resituirà una lista contenente i valori mantenuti dalle
variabili a
, b
e c
. Ad esempio:
(let* ( (a 1) (b 2) (c 3) ) (list 5 4 3 a b c) )
Questo codice crea la lista (5 4 3 1 2 3)
.
Per accedere ai valori in una lista si utilizzino le funzioni
car
e cdr
, che restituiscono rispettivamente
il primo elemento della lista e la porzion restante. Queste funzioni
spezzano la lista nella combinazione testa-coda precedentemente
menzionata.
car
restituisce il primo elemento della lista (la testa
della lista). La lista deve essere non-nulla. L'istruzione seguente
restituisce il primo elemento della lista:
(car '("first" 2 "third"))
che è:
"first"
cdr
restituisce la parte restante della lista dopo il
primo elemento (la coda della lista). Se vi è un solo elemento nella
lista restituisce una lista vuota.
(cdr '("first" 2 "third"))
restituisce:
(2 "third")
mentre l'istruzione seguente:
(cdr '("one and only"))
restituisce:
()
Bene, si è ora in grado di ottenere il primo elemento di una lista così
come la parte restante ma come si accede agli altri elementi di una
lista? Esistono parecchie funzioni di "convenienza" per accedere
alla testa della testa (caadr
) o analogamente alla coda di
una lista (cddr
), ecc.
La convenzione sui nomi di base è semplice: le a e le d rappresentano le teste e le code quindi
(car (cdr (car x) ) )
si potrebbe scrivere come:
(cadar x)
Per impratichirsi con le funzioni di accesso alle liste si provi a digitare quanto segue (su du un'unica linea se si utilizza la console), si utilizzino differenti varianti di car e cdr per accedere ai differenti elementi di una lista:
(let* ( (x '( (1 2 (3 4 5) 6) 7 8 (9 10) ) ) ) ; place your car/cdr code here )
Si provi ad accedere al numero 3 nella lista utilizzando solo due chiamate a funzione. Se si è in grado di farlo si è sulla buona strada per diventare un maestro di Script-Fu!
Nota | |
---|---|
In Scheme, un punto e virgola (";") indica un commento. Il segno stesso e quanto segue sulla stessa linea sono ignorati dall'interprete quindi si può utilizzare il punto e virgola per aggiungere commenti per rinfrescare la memoria quando si riprende uno script a distanza di tempo. |