3.3. Liste, liste e ancora liste

Abbiamo fatto pratica con le variabili e le funzioni, ora si entra nella palude fangosa delle liste di Scheme.

3.3.1. Definire una lista

Prima di approfondire le liste è necessario comprendere la differenza tra valori atomici e liste.

Abbiamo 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 singolo 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 semplicemente 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) ("è" ("grande" () ) ) )
        )
    )
    x
)
      

Si noti che dopo il primo apostrofo 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.

3.3.2. Come concepire le liste

È 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.

3.3.3. Creazione di liste attraverso la concatenazione (la funzione Cons)

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.

3.3.4. Definizione di una lista usando la funzione list

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

3.3.5. Accedere ai valori contenuti in una lista

Per accedere ai valori in una lista usare le funzioni car e cdr, che restituiscono rispettivamente il primo elemento della lista e la porzione restante. Queste funzioni spezzano la lista nel costrutto testa::coda precedentemente menzionato.

3.3.6. La funzione car

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 '("primo" 2 "terzo"))

che è:

"primo"

3.3.7. La funzione cdr

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 '("primo" 2 "secondo"))

restituisce:

(2 "terzo")

mentre l'istruzione seguente:

(cdr '("uno e solo"))

restituisce:

()

3.3.8. Accedere ad altri elementi di una lista

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 di un'unica riga se si utilizza la console), si utilizzino differenti varianti di car e cdr per accedere ai differenti elementi della lista:

        (let* (
                (x '( (1 2 (3 4 5) 6) 7 8 (9 10) )
                )
              )
              ;  metti il tuo codice car/cdr qui
        )
      

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] 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 utili a rinfrescare la memoria quando si riprende in mano uno script a distanza di tempo.