11: LE LISTE

TIPI DI DATO PIU' COMPLESSI

Immaginiamo di dover programmare un software per gestire i voti di una classe scolastica. La prima cosa che può venire in mente è di creare una variabile per ogni alunno. Siccome non sappiamo in anticipo i nomi degli studenti e il loro numero, probabilmente scriveremo qualcosa tipo Studente1, Studente2, Studente3, ecc. Ora per ogni studente dovremo probabilmente creare la variabile NomeStudente1, CognomeStudente1, VotiStudente1, AssenzeStudente1 ecc. In pratica il programma conterrebbe un numero enorme di variabili e sarebbe difficilissimo gestirle.

Per questo tutti i linguaggi di programmazione consentono di aggregare molte variabili in nuovi tipi di dati, che sono detti di solito tipi di dati derivati. I tipi di dati visti finora (int, float, bool) vengono detti tipi di dati atomici (o elementari): un tipo di dato derivato è quindi un insieme di dati di tipo atomico (ma anche a loro volta derivato) aggregati in un'unica variabile. A volte i dati componenti devono essere tutti dello stesso tipo (cioè tutti numeri o tutte stringhe), a volte possono essere di tipi diversi.

Tornando al nostro esempio, potrei definire uno studente come un oggetto formato da un dato nome (str), un cognome (str), un dato assenze, (int) e così via. Poi potrei definire una classe scolastica come un insieme di studenti, e così via. Certamente dovrei avere delle istruzioni in grado di selezionare il dato (atomico) che voglio leggere o scrivere. Così, se oggi ho interrogato il quinto studente della I A e gli ho messo 8, dovrei poter dire al linguaggio: assegna al voto dello studente 5 della classe I A il valore 8.

Vediamo le istruzioni che ci mette a disposizione Python.

LE LISTE

Una delle caratteristiche più potenti di Python è la presenza a livello nativo di un gran numero di tipi di dato derivati, e la semplicità della sintassi per manipolarli.

Il tipo di dato derivato più usato è senz'altro la lista (Inglese: list). Una lista è un insieme di dati (detti elementi) a cui è associato un ordine. Può contenere elementi di tipo diverso e può contenere anche più volte lo stesso elemento.

Per definire una lista si scrivono i suoi elementi, separati da virgole, tra parentesi quadre. Facciamo un po' di esempi direttamente in IDLE (al solito, potete omettere i commenti).


>>> citta = ["Roma", "Milano", "Firenze", "Bologna"]  # una lista di 4 string
>>> numeri = [1, 4, 5, 12, 23, 40]                    # una lista di 6 interi
>>> mista = [1, "due", 3, "quattro"]                  # elementi di tipo diverso
>>> numeri_uguali = [1, 4, 1, 6, 6, 4, 7, 7, 7, 1, 6] # elementi ripetuti
>>> vuota = []                                        # lista vuota, senza elementi
>>> citta

['Roma', 'Milano', 'Firenze', 'Bologna']

>>> numeri

[1, 4, 5, 12, 23, 40]

Notate che nella prima istruzione ho assegnato alla variabile città un'intera lista formata da quattro stringhe. Quando poi, più sotto, chiedo il valore della variabile citta, IDLE mi risponde ristampando le stringhe tra parentesi quadre. La cosa è simile per le altre liste.

Anche sulle liste possiamo compiere le operazioni di addizione e moltiplicazione per un intero, come per le stringhe, ed usare la funzione type(); continuate in IDLE così:


>>> citta

['Roma', 'Milano', 'Firenze', 'Bologna']

>>> numeri

[1, 4, 5, 12, 23, 40]

>>> citta + numeri

['Roma', 'Milano', 'Firenze', 'Bologna', 1, 4, 5, 12, 23, 40]

>>> 2 * citta

['Roma', 'Milano', 'Firenze', 'Bologna', 'Roma', 'Milano', 'Firenze', 'Bologna']

>>> type(mista)

<class 'list'>

Dato che quando si definisce una lista capita spesso di dover scrivere righe di codice molto lunghe, Python permette di andare a capo liberamete tra gli elementi della lista. Attenti a non dimenticare le virgole: suggerisco, quando si va a capo, di scrivere sempre la virgola come ultimo carattere della riga superiore. IDLE ci fa capire che la riga successiva è una continuazione scrivendo i caratteri più a destra.


>>> lista_lunga = ["uno", 2, "tre", 4,
	       "cinque", 6, "sette",
	       8, "nove", 10]

FUNZIONI SULLE LISTE

Sugli oggetti di tipo lista possiamo applicare un gran numero di funzioni. La maggior parte di esse viene chiamata però non con la sintassi che già conosciamo, ma con una sintassi simile a quella che abbiamo già visto per i moduli.

Se nome_lista è una variabile di tipo lista, possiamo chiamare una funzione nome_funzione applicata ad essa con la sintassi:

nome_lista.nome_funzione(argomenti ...)

In questo modo il nome della lista a cui stiamo applicando la funzione non compare tra gli argomenti della funzione (cioè tra le parentesi), ma diventa una specie di parametro "speciale" che si scrive davanti al nome di funzione. Per conoscere tutte le funzioni di questo tipo possiamo digitare in IDLE:


>>> help(list)

Vediamo le più usate (nella tabella supponiamo che la variabile l contenga una lista):

Funzione Significato
l.append(a) Aggiunge l'elemento a alla fine della lista l
l.insert(n, a) Aggiunge l'elemento a al posto n nella lista (i posti sono numerati da 0, vedi sotto)
l.remove(a) Rimuove dalla lista l l'elemento a. Se a non appartiene alla lista provoca un ValueError
l.sort() Ordina gli elementi della lista in ordine alfabetico o numerico
l.reverse() Inverte l'ordine degli elementi della lista
l.count(a) Restituisce (come int) il numero di occorrenze di a nella lista (cioè quante volte l'elemento a è contenuto in l, 0 se non è presente)
l.pop(a)
l.pop()
Rimuove l'elemento a dalla lista e lo restituisce come risultato della funzione. Usata senza parametro rimuove l'ultimo elemento
l.clear() Elimina tutti gli elementi: la lista l diventa vuota
ESERCIZIO 11.1: Supponiamo di avere già definito la lista citta come nell'esempio precedente. In IDLE date i seguenti comandi. Dopo ogni comando cercate di immaginare come è cambiata la lista e controllate (nell'esercizio non è mostrato l'output di IDLE)
>>> citta.append("Venezia")
>>> citta
>>> citta.remove("Roma")
>>> citta
>>> citta.insert(1, "Roma")
>>> citta
>>> citta.sort()
>>> citta
>>> citta.reverse()
>>> citta.count("Torino")
>>> citta.clear()
>>> citta

Altre funzioni devono invece essere chiamate con la sintassi che abbiamo già visto, inserendo la lista come parametro della funzione stessa.

Funzione Significato
len(l) Restituisce (come int) la lunghezza della lista, cioè il numero dei suoi elementi
max(l) Restituisce l' elemento più grande dalla lista (in ordine alfabetico o numerico). Se gli oggetti non sono confrontabili (ad es. numeri e stringhe) da un TypeError
min(l) Restituisce l'elemento più piccolo della lista
sum(l) Restituisce la somma di tutti gli elementi della lista. Devono essere tutti numeri altrimenti da un TypeError

E' un'altra situazione che può disorientare un principiante: alcune funzioni vanno chiamate in un modo, altre in un modo differente. Il motivo di tutto ciò è piuttosto oscuro, e di difficile comprensione per chi non abbia già una certa esperienza con i linguaggi di programmazione. Per il momento è utile cercare di fare molta pratica in modo da ricordare bene l'uso delle funzioni sulle liste e non confondersi sulla loro sintassi.

ESERCIZIO 11.2 Provate da soli ad usare le funzioni su una lista. Lavorate in IDLE ed eseguite nell'ordine queste operazioni:
  1. Definite una list capitali vuota
  2. Aggiungete, una dopo l'altra, "Roma", "Parigi", "Londra"
  3. Aggiungete come secondo elemento "Madrid" (attenti! Qui non potete usare append())
  4. Controllate di quanti elementi è composta la lista (dovrebbero essere 4)
  5. Invertite l'ordine delle città
  6. Eliminate "Roma"
  7. Aggiungete per quattro volte "Parigi" in fondo
  8. Contate quante volte "Parigi" è contenuta nella lista (dovrebbero essere 5)
  9. Controllate ancora di quanti elementi è composta la lista (dovrebbero essere 7)
  10. Ordinate le città in ordine alfabetico
  11. Eliminate tutti gli elementi dalla lista
SOLUZIONI

LA SELEZIONE DEGLI ELEMENTI

Come facciamo a leggere o scrivere un singolo elemento appartenente ad una lista? La cosa è semplice: ad ogni elemento è associato un numero, detto indice, che corrisponde alla sua posizione nella lista. Il primo elemento ha indice 0 (di nuovo questa tradizione deriva dal linguaggio C) quindi l'ultimo ha indice uguale alla lunghezza della lista meno 1 (si può ottenere con la funzione len()).

Per accedere ad un elemento si deve scrivere il nome della lista e subito dopo l'indice dell'elemento tra parentesi quadre, secondo la sintassi nome_lista[indice] sia in lettura che in scrittura. Proviamo ancora in IDLE:


>>> citta = ["Roma", "Milano", "Venezia", "Firenze", "Bologna"]
>>> citta[4]

'Bologna'

>>> citta[2] = "Napoli"
>>> citta

['Roma', 'Milano', 'Napoli', 'Firenze', 'Bologna']

>>> citta[0] + citta[1]

'RomaMilano'

Nella seconda riga ho letto il quinto elemento della lista citta (che ha indice 4); nella terza ho assegnato al terzo elemento il nuovo valore "Napoli", nell'ultima ho fatto la somma del primo e secondo elemento (concatenando le stringhe).

La funzione index() cerca un elemento nella lista e ci restituisce il suo indice (o l'indice della sua prima occorrenza se è contenuto più volte). Se l'elemento non è presente ci da un ValueError.


>>> citta.index("Roma")

0

>>> citta.index("Firenze")

3

>>> citta.index("Torino")

Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    citta.index("Torino")
ValueError: 'Torino' is not in list

E' molto facile confondersi usando gli indici. Ricordate sempre che il primo elemento ha indice 0, l'ennesimo elemento ha indice n - 1, l'ultimo len - 1. Se scriviamo un indice che non esiste otteniamo un nuovo tipo di errore: IndexError.

Infine possiamo utilizzare gli operatori booleani in e not in che ci dicono se un elemento appartiene o no ad una lista.


>>> "Roma" in citta

True

>>> "Catanzaro" in citta

False

>>> "Milano" not in citta

False

>>> "Sassari" not in citta

True
ESERCIZIO 11.3: Di nuovo eseguiamo una serie di comandi in IDLE per imparare ad usare la selezione degli elementi di una lista. Dopo ogni comando cercate di immaginare come è cambiata la lista e controllate digitando il suo nome.
>>> numeri = [1, 3, 5, 7, 9]
>>> numeri[0] = numeri[2]
>>> numeri[1] = numeri[3] + numeri[4]
>>> numeri.append(numeri[3])
>>> numeri[3] = sum(numeri)
>>> numeri[2] = numeri[0] * numeri[1]
>>> numeri.sort()
ESERCIZIO 11.4 Ora provate voi
  1. create una list numeri con i primi 5 numeri pari (partendo da 2)
  2. assegnate al primo elemento il doppio del secondo (attenti agli indici!)
  3. assegnate all'ultimo elemento il triplo del terzo
  4. raddoppiate il terzo elemento (cioè assegnategli il doppio di sè stesso)
A questo punto dovreste avere:

>>> numeri

[8, 4, 12, 8, 18]
SOLUZIONI

Ora proviamo ad usare quanto abbiamo imparato in un programma.

ESERCIZIO 11.5 PROGRAMMA PASSO PASSO: statistiche_voti.py
Scrivere il programma a partire da questo schema:
  1. create la list materie = ["Italiano", "Matematica", "Storia", "Geografia", "Inglese"]
  2. create la list voti = [ 8, 6, 5, 9, 8]
  3. create un ciclo while con una variabile contatore i. La variabile dovrà variare tra 0 e la lunghezza di materie (non compresa).
  4. nel corpo del ciclo, stampate la materia i-esima ed il corrispondente voto
  5. dopo il ciclo stampare la media, il voto più alto ed il più basso: questi dati si possono ottenere con le funzioni che abbiamo visto applicate alla lista voti. ATTENZIONE: per calcolare la media è necessario dividere per il numero dei voti. Potreste essere tentati di dividere per 5, ma dal punto di vista di un programmatore è meglio ricavare la lunghezza della lista voti mediante la funzione ... In questo modo, se dovessimo aggiungere o togliere altre materie, non sarebbe necessario modificare il programma.
Questo dovrebbe essere l'output:
Il tuo voto in Italiano e' 8
Il tuo voto in Matematica e' 6
Il tuo voto in Storia e' 5
Il tuo voto in Geografia e' 9
Il tuo voto in Inglese e' 8
La tua media e' 7.2
Il tuo voto piu' alto e' 9
Il tuo voto piu' basso e' 5
ESERCIZIO 11.6: Inseriamo noi i voti
Modificate il programma in modo che consenta all'utente di inserire i propri voti:
  1. la lista voti deve essere inizialmente vuota
  2. per inserire i voti è necessario un altro while simile a quello dell'esercizio precedente. Nel corpo del ciclo chiedete in input all'utente il suo voto nell' i-esima materia ed aggiungetelo in coda a voti. Esempio di I/O (l'output del programma è stampato in blu e le risposte dell'utente sono in nero):
    Inserisci il tuo voto in Italiano 8
    Inserisci il tuo voto in Matematica 8
    	. . .
    
    ATTENZIONE: la funzione input(), a differenza della print() prende come argomento una sola stringa, quindi dovete ottenere il prompt concatenando più stringhe con il + (e facendo attenzione agli spazi) ...
  3. continuate poi come nell'esercizio precedente
ESERCIZIO 11.7 (Più difficile) Trovare la materia per il voto più alto e più basso
Vogliamo sapere, oltre al voto più alto e più basso, anche in quale materia sono stati ottenuti. Modificate la fine del programma così:
  1. trovate il più basso dei voti e memorizzatelo in una variabile m
  2. cercate in voti l'indice di m e memorizzatelo in una variabile ind
  3. a questo punto nella corrispondente posizione di materie c'è la materia in cui avete preso quel voto, quindi potete stampare:
    Il tuo voto piu' basso e' 5 in Matematica
  4. ripetete il procedimento per il voto più alto
ATTENZIONE: Se ci sono più materie con il voto minimo questo semplice programmino troverà soltanto la prima. Elencarle tutte è piuttosto difficile, chi vuole può provarci ...
SOLUZIONI

Fine della lezione