Python

Le liste              

Eccoci a parlare di un argomento estremamente importante: le liste ovvero la sequenza  più generica che incontrerete in questo linguaggio. Esse sono collezioni ordinate di elementi di qualsiasi natura e non hanno limiti di dimensioni. Ancora più significativo il fatto che sono modificabili e questo, come vedremo, ci permette molta libertà di azione.  Concettualmente ricordano array e liste di altri linguaggi, tenendo sempre presente, nell'ambito di Python, che ci troviamo di fronte ad entità aventi natura dinamica.
Definire una lista è molto semplice:

Lista = []
Lista = [Espressione...]

La prima riga crea una lista vuota, la seconda ci indica, in qualche modo, la grande varietà di entità che possiamo infilare in una lista. Vedremo più avanti la cosiddetta "list comprehension", come altro sistema per generare liste.

Alcuni esempi pratici:

>>> L1 = [1,2,3,4,5]
>>> L2 = ['a', 'b']
>>> L3 = [1, 'a', "ciao", 1+1]
>>> L4 = [1,2, ['c', 'i', 'a', 'o', 4]]

Nell'ultima riga la lista contiene come ultimo elemento un'altra lista.

Le liste sono indicizzate, come potrete intuire gli indici sono numeri in sequenza da 0 ad n-1 con n che è il numero degli elementi. Quindi una lista con 10 elementi sarà indicizzata tramite gli interi che vanno da 0 a 9 e l'elemento, ad esempio, all'indice 3 visivamente è il quarto, partendo idealmente da sinistra verso destra.

Essendo una sequenza fondamentalmente, le liste sono soggette a tutte quelle operazioni generiche che si possono adoperano appunto con le sequenze:

>>> L1 = [1,2,3,4,5]
>>> print (len(L1))
5

Giocare con gli indici può essere abbastanza vario. Python ammette anche gli indici negativi:

>>> print (L1[-1])
5
>>> print (L1[-5])
1

Per cui visivamente si avrebbe questa situazione:

Lista 1 2 3 4 5
Indici positivi 0 1 2 3 4
Indici negativi -5 -4 -3 -2 -1

Altra possiblità offerta è quella di lavorare tramite "slicing" ovvero definendo degli intervalli. Sintatticamente la definizione è la seguente:

[x : y] ove x ed y possono essere, indipendentemente, assenti. La notazione indicata comprenderà tutti gli elementi da x a y-1. L'assenza dell'elemento di sinistra sottoinderà che si parte da 0, l'assenza di quello di destra indica che si va fino alla fine della lista. Gli esempi che seguono sono puramente indicativi, potete, anzi, dovete, sbizzarrirvi nel crearvene dei vostri e prendere dimestichezza con una notazione potente ma che può anche indurre in confusione, se non ci fate un po' l'occhio:

 >>> print(L1[1:3])
[2, 3]
>>> print(L1[0:])
[1, 2, 3, 4, 5]
>>> print(L1[:3])
[1, 2, 3]
>>> print(L1[:])
[1, 2, 3, 4, 5]
>>> print(L1[-2:-4])
[]
>>> print(L1[-4:-2])
[2, 3]

len ci restituisce la lunghezza di una lista, ovvero il numero dei suoi elementi.

Accedere ad un singolo elemento è semplice, grazie all'operatore [] che racchiude al suo interno l'intero che si riferisce all'indice che ci interessa:

>>> print(L1[3])
4
>>> print(L1[8])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range

Il primo esempio ci restuisce l'elemento avente indice 3, il secondo ci va vedere cosa succede se richiamiamo un indice fuori dal range previsto (la lista è quella definita nelle righe più in alto).

L'operatore normalmente dedicato alle addizioni, il +, ci permette di concatenare nuovi elementi alla nostra lista:

>>> L1 = [1,2,3,4,5]
>>> L2 = L1 + [4,5,6]
>>> print(L2)
[1, 2, 3, 4, 5, 4, 5, 6]
>>> L1
[1, 2, 3, 4, 5]

Esiste poi una serie di metodi propri delle liste:

append(x) - piazza l'elemento x in coda alla lista é lo stesso comportamento del +):
>>> L1 = [1,2,3,4]
>>> L1.append(5)
>>> L1
[1, 2, 3, 4, 5]

extend(L) - estende la lista accodando gli elementi di un'altra lista
>>> L1 = [1,2,3,4]
>>> L1.extend([6,7,8])
>>> L1
[1, 2, 3, 4, 5, 6, 7, 8]

insert(x, n) - inserisce l'elemento x nella posizione n. Banalmente, riprendendo l'esempio precedente:
>>> L1
[1, 2, 3, 4, 5, 6, 7, 8]
>>> L1.insert(3,99)
>>> L1
[1, 2, 3, 99, 4, 5, 6, 7, 8]

remove(x) - rimuove il primo elemento il cui valore è x, viene restituito un errore se non ne esiste nessuno.
>>> L1 = [1,2,3,4,5]
>>> L1.remove(2)
>>> L1
[1, 3, 4, 5]
>>> L1.remove(6)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: list.remove(x): x not in list

Se invece volete elminare di colpo tutti gli elementi di una lista allora clear() è il metodo che fa per voi.
>>> L1.clear()
>>> L1
[]

copy() - effettua una copia della lista:
>>> L1 = [1,2,3,4]
>>> L2 = L1.copy()
>>> L1
[1, 2, 3, 4]
>>> L2
[1, 2, 3, 4]

pop(n) - rimuove e restituisce l'elemento all'indice n. E' possibile anche omettere l'indice ed in tal caso viene eliminato e restituito l'ultimo elemento.
>>> L1 = [1,2,3,4,5]
>>> print(L1.pop(3))
4
>>> L1
[1, 2, 3, 5]

in alternativa a pop possiamo usare del che elimina un elemento ad un certo indice senza restituirlo e può essere usato anche via slice, come vedremo.  Va specificato che del non è un metodo ma una vera e propria istruzione che può essere usata per eliminare completamente una variabile così che non più possibile riferirsi ad essa, il che è esemplificato nell'ultimo frammento di codice:

>>> L1 = [1,2,3,4]
>>> del L1[2]
>>> L1
[1, 2, 4]

>>> L1 = [1,2,3,4,5,6,7,8,9,0]
>>> del L1[:5]
>>> L1
[6, 7, 8, 9, 0]

>>> L1 = [1,2,3,4,5]
>>> del L1[:]
>>> L1
[]
>>> del L1
>>> L1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'L1' is not defined

Nell'ultimo esempio abbiamo usato del prima per svuotare completamente la lista e poi per eliminarla definitivamente, come si deduce dal messaggio d'errore e in particolare dalla parte evidenziata in rosso.

index(n) - restituisce l'elemento avente indice n o un errore se n è fuori range. E' equivalente da un punto di vista pratico, di [].
>>> L1 = [1,2,3,4,5]
>>> print(L1.index(3));
2
>>> print(L1.index(8))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: 8 is not in list

count(x) - restituisce il numero di occorrenze di x.
>>> L1 = [1,2,1,3,1,4,1]
>>> print(L1.count(1))
4

Ovviamente se nessun elemento viene trovato il risultato è 0, non un errore.

sort() - ordina la lista in senso crescente, reverse() inverte gli elementi. Entrambi questi metodi lavorano sullalista originale cambiandola definitivamente.
>>> L1 = [2,5,7,1,9,0,4]
>>> L1.sort()
>>> L1
[0, 1, 2, 4, 5, 7, 9]
>>> L1.reverse()
>>> L1
[9, 7, 5, 4, 2, 1, 0]

LIST COMPREHENSION

Si tratta di un comodo sistema per generare liste basandoci su altre sequenze. E' una tecnica che viene sempre più supportata dai linguaggi di programmazione, vuoi che sia compresa nativamente in fase di progettazione, vuoi che sia aggiunta successivamente. L'elenco, piuttosto nutrito, comprende linguaggi classici come Ruby, Erlang o Scala, ed altri più recenti, come F#, Falcon e Julia.
In Python la List comprehension (se vi piace potete usare la forma nostrana "comprensione di liste" che ho sentito pochissime volte anche da parte di programmatori di lingua italiana) consiste di una o più espressioni for all'interno di una coppia di parentesi quadrate. Un loop che permette di definire elementi attraverso determinate selezioni. Di seguito alcuni esempi di base, tenete presente che si può arrivare alivelli piuttosto alti di complicazione:

>>> L1 = [x * 3 for x in [1,2,3,4]]
>>> L1
[3, 6, 9, 12]

>>> L1 = [x + y for x in [2,3,4] for y in [10,20,30]]
>>> L1
[12, 22, 32, 13, 23, 33, 14, 24, 34]

>>> L1 = [x + y for x in [2,3,4] for y in [10,20,30] if (x + y) < 20]
>>> L1
[12, 13, 14]

E' certamente una strada preferibile piuttosto che usare una serie infinita di cicli innestati e di append.