Lisp – collezioni – 1

l7Un argomento che è esso stesso una collezione; ma seguo Peter Seibel 😀
Come tutti anche il Lisp ha un suo modo di collezionare più valori in un unico oggetto. E come capita spesso le cose non sono sempre fatte allo stesso modo. Quelli standard sono arrays, liste e tuple, poi ci sono hash tables, arrays associativi, mappe e dizionari.

Il Lisp è famoso per le sue liste, ma non ci sono solo loro. E siccome sono comode vengono spesso usate dove sarebbe meglio usare arrays e hash tables, nèh! 😉

Adesso –dice Peter– ci concentriamo su vettori e hash tables; ma i vettori condividono parecchie caratteristiche con le liste, anzi sono entrambe sequenze. Per cui ne parleremo prossimamente.

Vettori

I vettori (arrays monodimensionali; una volta gli arrays da noi fortranisti erano chiamati matrici, non so se è ancora valido o sono finiti nel dimenticatoio come giumpare) sono la collezione base di interi indicizzati e possono essere a dimensione fissa o ridimensionabili.

Per creare un vettore fixed-size si usa vector:

(vector)     ==> #()
(vector 1)   ==> #(1)
(vector 1 2) ==> #(1 2)

La sintassi #(...) è quella standard usata da print e read. Si può usare ma non esiste un modo standard per modificarli e allora meglio make-array per creare vettori che dovranno essere modificati.

make-array è molto più generale, permette di creare arrays multidimensionali,. di dimensioni fisse o variabili. Il solo argomento richiesto è una lista contenente le dimensioni dell’array. Siccome il vettore è monodimensionale questa lista conterrà un solo numero, la dimensione del vettore. In questo caso make-array accetta un numero invece della lista.
Senza altri argomenti make-array crea il vettore da inizializzare prima di potervi accedere. per creare un vettore con gli elementi predefiniti a un certo valore si passa :initial-element. Ecco come creare un vettore di 5 elementi inizializzati a nil:

c11-0

make-array è anche la funzione per creare vettori ridimensionabili. In questo caso il vettore deve contenere anche il suo numero di elementi, nel fill pointer, così chiamato perché è l’indice del prossimo dato da inserire. Ecco come creare un vettore di 5 elementi, vuoto:

c11-1

per aggiungere elementi a questo vettore si usa vector-push-extend che funziona come vector-push ma espande automaticamente il vettore se necessario. Ragion per cui :fill-pointer e :adjustable possono essere omessi se pasticci.

Sottotipi di vettori

I vettori visti finora sono generali, nel senso che possono contenere qualunque tipo di oggetti. È possibile creare vettori specializzati che possono contenere solo certi tipi di oggetti. Sono più compatti e veloci, vediamone un paio.

Uno di questi l’abbiamo già incontrato, la stringa, che contiene caratteri. le stringhe sono così importanti da avere una loro sintassi (le virgolette) e funzioni loro specifiche, già viste. Ma ci sono funzioni per i vettori che valgono anche per loro, come stiamo per vedere.

Le stringhe letterali come “foo” sono come vettori letterali scritti con la sintassi #(), la loro dimensione è fissa e non possono essere mutate. Tuttavia posso usare make-array per creare stringhe ridimensionabili aggiungendo la keyword :element-type. Questa keyword prende un argomento descrittivo, ce ne sono millantamila, a noi serve solo sapere che per le stringhe il simbolo è character; il simbolo dev’essere quotato altrimenti viene considerato una variabile. Esempio di stringa ridimensionabile vuota:

c11-2

Ci sono anche i bit-vectors, vettori i cui elementi possono essere solo zero e uno; hanno un trattamento speciale, si leggono/scrivono come #*00001111 e ci sono intere librerie per trattarli. Il tipo di descrittore da passare con :element-type è —indovina?bit.

Vettori come sequenze

Come già detto vettori e liste sono sequenze; le funzioni seguenti sono funzioni per le sequenze e quindi possono essere usate con loro.

length ritorna la lunghezza di una sequenza; per i vettori con un fill pointer ritorna questo valore, altrimenti il numero di caratteri che contiene (mi sembrano coincidenti, devo pensarci su).
elt (da element) permette di accedere a un elemento individuale della sequenza attraverso in indice intero compreso tra 0 (incluso) e la lunghezza della sequenza (escluso) e ritorna l’elemento corrispondente. Segnala un errore se sei fuori. Esempio:

c11-3

elt permette anche di settare sul posto, esempio:

c11-4

Funzioni per iterare le sequenze

Oltre a length, elt e il setf di elt ci sono parecchie funzioni per le sequenze. Un gruppo di queste consente certe operazioni di ricerca e filtro di elementi senza uno scrivere un specifico ciclo, eccone un riassunto:

Name         Required Arguments             Returns
count        Item and sequence              Number of times item appears in sequence
find         Item and sequence              Item or nil
position     Item and sequence              Index into sequence or nil
remove       Item and sequence              Sequence with instances of item removed
substitute   New item, item, and sequence   Sequence with instances of item replaced 
                                            with new item

Uh! qualche esempio:

c11-5

Notare che remove e substitute ritornano sempre la sequenza del tipo passato.

Si può modificare il comportamento di queste funzioni in parecchi modi con keyword arguments. Per esempio si può usare :test per passare una funzione che accetta 2 argomenti e ritorna un booleano; se fornita verrà usata per comparare item per ogni elemento invece del test di eguaglianza standard eql. Con :key posso passare una funzione con un argomento da chiamarsi per ogni elemento della sequenza per estrarre una key che dev’essere comparata con l’elemento. Notare tuttavia che find che ritorna elementi della sequenza continua a ritornare l’elemento corrente e non solo la chiave estratta. Capito molto poco, anzi niente, nada, zilch 👿

c11-6

Per limitare queste funzioni a una sub-sequenza si possono fornire :start e :end. Se :end è nil o omesso viene considerata la lunghezza della sequenza.
Si può passare :from-end per esaminare gli elementi in ordine inverso; funziona solo con find e position:

c11-7

Tuttavia :from-end altera remove e substitute in congiunzione con :count, usato per indicare quanti elementi rimuovere o sostituire:

c11-8

E mentre :from-end non può cambiare il risultato della funzione count altera l’ordine con cui sono passati :test e :key alle funzioni, con possibili effetti collaterali, esempio:

c11-9

Tabella riassuntiva per questi argomenti:

Argument   Meaning                                            Default
:test      Two-argument function used to compare item 
           (or value extracted by :key function) to element.  eql
:key       One-argument function to extract key value from 
           actual sequence element. nil means use element 
           as is.                                             nil
:start     Starting index (inclusive) of subsequence.         0
:end       Ending index (exclusive) of subsequence. 
           NIL indicates end of sequence.                     nil
:from-end  If true, the sequence will be traversed in 
           reverse order, from end to start.                  nil
:count	   Number indicating the number of elements to 
           remove or substitute or nil to indicate all 
           (remove and substitute only).                      nil

Panico? No, dai, continua, prossimamente :mrgreen:

Posta un commento o usa questo indirizzo per il trackback.

Trackback

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo di WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Google photo

Stai commentando usando il tuo account Google. Chiudi sessione /  Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.

%d blogger hanno fatto clic su Mi Piace per questo: