Sappiamo già che Python (come tutti i linguaggi di programmazione) permette di aggregare più dati semplici formando dei
tipi di dati più complessi che sono detti genericamente containers o collections
(in Python abbiamo le liste, tuple, ecc.) Un container permette di accedere ai suoi elementi
per mezzo degli indici racchiusi dalle parentesi quadre (ad esempio, se ls
è una lista, ls[0]
è il suo
primo elemento, ls[1]
il secondo e così via).
Python ha però enormemente sviluppato questa possibilità, dando all'utente la possibilità di selezionare non solo singoli elementi, ma intere sottosequenze della sequenza principale, tramite una serie di regole sintattiche che sono state ribattezzate slice notation (cioè "notazione a fettine"). Vediamo in questa lezione le possibilità che ci offre.
Possiamo usare come indice un numero negativo: in questo caso gli indici partono dalla fine della lista: l'elemento -1 è l'ultimo, l'elemento -2 il penultimo e così via. Proviamo questi comandi in IDLE:
>>> citta = ["Roma", "Milano", "Napoli", "Firenze", "Bologna"] >>> citta[-1]
'Bologna'
>>> citta[-2]
'Firenze'
>>> citta[-10]
Traceback (most recent call last): File "<pyshell#4>", line 1, in <module> citta[-10] IndexError: list index out of range
>>> nome = "Massimiliano" >>> nome[-1]
'o'
>>> nome[-2]
'n'
>>> nome[-len(nome)]
'M'
Nella terza istruzione ho volutamente provocato un IndexError, mentre l'ultima è un modo più complicato di indicare il primo elemento.
Possiamo selezionare un'intera sottosequenza di una lista indicando tra parentesi quadre due indici separati
da un carattere ":
"; come al solito il primo indice è quello iniziale (incluso), mentre il secondo è quello
finale (escluso). Continuate così:
>>> citta[1:3]
['Milano', 'Napoli']
>>> nome[3:9]
'simili'
>>> nome[5:5]
''
>>> nome[5:4]
''
Notate le ultime due istruzioni: se il secondo indice è uguale o minore del primo ottengo una sequenza vuota. La cosa funziona anche con gli indici negativi, che si possono anche usare insieme a quelli positivi.
>>> citta[-3:-1] # gli elementi dal terzultimo all'ultimo
['Napoli', 'Firenze']
>>> citta[-1:-3] # il contrario (lista vuota)
[]
>>> nome[3:-2] # le lettere di 'Massimiliano' dalla terza alla penultima
'similia'
>>> nome[5:-2] # dalla quinta alla penultima
'milia'
>>> nome[10:-2] # stringa vuota
''
Vi ripeto che se il secondo elemento (in qualunque modo esso sia indicato) occupa una posizione uguale o minore del primo si ottiene una sottosequenza vuota). E' il caso dell'ultimo esempio, dove il primo indice (10) rappresenta la nona lettera di "Massimiliano" (cioè la "a") e il secondo (-2) la terzultima (la stessa "a").
Per includere nella selezione l'ultimo elemento della sequenza si deve omettere il secondo indice, lasciando però i due punti; similmente, anche il primo indice può essere omesso, indicando il primo elemento della sequenza.
>>> citta[:3]
['Roma', 'Milano', 'Napoli']
>>> nome[-5:]
'liano'
Ed infine possiamo aggiungere un terzo numero, sempre separato da ":
", che indica lo "step"
con cui dobbiamo selezionare gli elementi. Per ricordare queste regole basta notare che sono identiche a quelle che si usano
per i parametri della funzione range()
(vedi qui).
>>> nome[2:8:2] # dalla terza lettera alla settima ogni due lettere
'sii'
>>> nome[::2] # tutta la stringa ogni due lettere
'Msiiin'
>>> nome[::-3] # dall'ultima lettera alla prima ogni tre lettere
'oims'
Insomma, potete capire che le possibilità sono davvero molte e forse è anche difficile capire l'utilità di tutte combinazioni possibili. Nel seguito cercherò di mostrarvene alcune.
>>> numeri = list(range(100))
che ci da la lista di tutti i numeri da 0 a 99. Ora provate ad immettere queste istruzioni, cercando, prima di dare invio
per ognuna, di indovinare il loro risultato (che qui ometto).
>>> numeri[5:20]
>>> numeri[:50]
>>> numeri[-5:-1]
>>> numeri[50:-3]
>>> numeri[-99:5]
>>> numeri[10:30:3]
>>> numeri[10::4]
ESERCIZIO 1.2: Ora facciamo l'esercizio inverso: vi propongo delle sottosequenze della lista numeri
e voi
dovete scrivere delle istruzioni che restituiscano le sottosequenze date:
[25, 26, 27, 28, 29]
[30, 32, 34, 35]
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 5', 55, 60, 65, 70, 75, 80, 85, 90, 95]
[0, 2, 4, 6, 8]
[99, 98, 97, 96, 95, 94, 93, 92, 91, 90]
Ricordiamo che per Python stringhe e tuple sono oggetti immutable (cioè che non si possono più modificare una volta che è stato loro assegnato un valore): questo vuol dire che per mezzo degli indici possiamo solo leggere i dati ma non modificarli. Una tecnica molto usata per aggirare questa limitazione è quella di eseguire delle operazioni sull'oggetto e poi riassegnare il risultato a sè stesso. Ad esempio, proviamo a togliere qualche carattere ad una stringa:
>>> s = "Luigi" >>> s = s[:-1] >>> s
'Luig'
>>> s = s[1:] >>> s
'uig'
nella prima istruzione abbiamo riassegnato ad s
tutti i suoi caratteri dal primo al penultimo,
nella seconda i suoi caratteri dal secondo in poi. Vediamo ora come cambiare una lettera in una stringa:
>>> s = "mastino" >>> s = s[:2] + "t" + s[3:] >>> s
'mattino'
analizzate l'esempio e cercate di capire come ho fatto ad ottenere il risultato. Infine vediamo un programmino che toglie una per una tutte le lettere aad una stringa:
import random
alfabeto = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
random.seed()
while len(alfabeto) > 0:
print("La stringa", alfabeto, "contiene", len(alfabeto), "caratteri")
input("Premi INVIO per togliere una lettera")
ind = random.randrange(len(alfabeto))
print("Ho tolto la", alfabeto[ind])
alfabeto = alfabeto[:ind] + alfabeto[ind + 1:]
print("Fine")
Dovreste ricordare che il modulo random
importato nella #1 contiene alcune funzioni per
la generazione di numeri casuali. Notate che ho usato la sintassi import random
che ci obbliga a chiamare tali
funzioni anteponendo il nome del modulo (come nella #4 e nella #8). Questo è
un po' più scomodo rispetto all'istruzione from random import *
che ho usato nel primo tutorial, ma è la prassi
comune nei programmi "seri" e quindi conviene abituarcisi (vedi qui). Se non
ricordate a cosa servono le funzioni seed()
e randrange()
potete ripassarlo
qui. Le righe che ci interessano sono la #8 che
genera un indice casuale (corrispondente ad una lettera della stringa alfabeto
) e la #10
che toglie quella lettera dalla stringa.
Usiamo ora le nostre nuove conoscenze per implementare un programma capace di coniugare i verbi regolari: vogliamo quindi ottenere un I/O di questo tipo (ho scritto in blu l'input dell'utente):
Immetti un verbo regolare della prima coniugazione all'infinito:
amare
Io amo Tu ami Egli ama Noi amiamo Voi amate Essi amano
pronomi = ("Io", "Tu", "Egli", "Noi", "Voi", "Essi")
desinenze = ("o", "i", "a", "iamo", "ate", "ano")
La prima contiene i pronomi delle sei persone del verbo, mentre la seconda contiene le desinenze corrispondentiinput()
che chieda all'utente di scrivere un verbo regolare della prima
coniugazione, assegnando la stringa digitata alla variabile verbo
;for
con il contatore i
che varia da 0 a 5 (dovete usare un range()
...);verbo
meno le
ultime tre) con la corrispondente desinenza nella nostra tuple. Dovrebbe essere abbastanza facile
ottenere questo usando la slice notation.
coniugazioni = ("are", "ere", "ire")
desinenze
, in modo da avere le desinenze per tutte e tre le coniugazioni.
ATTENZIONE! In questo modo desinenze
diventa una tuple che contiene a sua volta altre tuple, quindi le
singole desinenze andranno chiamate con un doppio indice...
desinenze = (("o", "i", "a", "iamo", "ate", "ano"), # prima coniugazione
("o", "i", "e", "iamo", "ete", "ono"), # seconda coniugazione
("o", "i", "e", "iamo", "ite", "ono")) # terza coniugazione
input()
, togliendo il riferimento alla prima
coniugazione;slice notation
) all'interno
della tuple coniugazioni
: utilizzate la funzione index()
(vedi
qui) che resituisce l'indice della desinenza all'interno
della tuple, assegnandola alla variabile num_con
(0 = prima, 1 = seconda, 2 = terza).Questi esercizi dovrebbero darvi un'idea delle difficoltà cui vanno incontro i programmatori che vogliono scrivere software che trattano il linguaggio umano (come ad esempio i traduttori automatici). Infatti notiamo che, anche all'interno dei verbi regolari, ci sono alcune eccezioni. Provate ad inserire i verbi "giocare" o "mangiare" e vedrete che il nostro programmino fallirà miseramente.
desinenze
: la quarta verrà usata per i verbi in
"care" e "gare", la quinta per i verbi in "iare";
desinenze = (("o", "i", "a", "iamo", "ate", "ano"),
("o", "i", "e", "iamo", "ete", "ono"),
("o", "i", "e", "iamo", "ite", "ono"),
("o", "hi", "a", "hiamo", "ate", "ano"),
("o", "", "a", "amo", "ate", "ano"))
if
per controllare se siamo
in uno dei casi particolari;in
per controllare la seconda ipotesi) siamo nel primo caso particolare e dobbiamo porre
num_con = 3
per usare la nostra "quarta coniugazione";num_con = 4
.Fine della lezione