Prima di iniziare:
Ecco alcune linee guida da seguire per permetteterci di aiutarvi più velocemente a risolvere i problemi con il vostro codice:
- Date alle variaibli nomi descrittivi. Nella vostra testa può essere chiarissimo che
Msia il massimo mentremsia il minimo ma per noi no. Dover capire cosa ogni variabile contenga ruba energie a noi e tempo a voi. Invece diMemusate piuttosto:
n_max = ... # maximum of the list
n_min = ... # minimum of the list
- Gli esercizi possono essere risolti utilizzando solo ciò che è stato fatto a lezione. Utilizzare caratteristiche del linguaggio non viste a lezione può sembrare una mossa da pro ma in realtà state sfruttando la flessibilità del linguaggio invece di risolvere veramente il problema. Provate queste soluzioni avanzate solo dopo aver risolto il problema usando gli argomenti fatti in aula.
- Tenete traccia dei vari step della soluzione. Se avete risolto parte del problema (e.g. avete del codice che trova il massimo di una lista) create una nuova cella e copia-incollate il codice prima di modificarlo. Questo vi permetterà di avere un checkpoint durante la soluzione del problema. Non c'è niente di peggio di cancellare del codice funzionante e non riuscire a riscriverlo.
Esercitazione 2: Ordinare una lista di numeri¶
Lo scopo di questa esercitazione è imparare ad ordinare una lista di numeri in ordine crescente o decrescente. Esistono innumerevoli algoritmi di ordinamento con diversi pregi, difetti e complessità ma in questa esercitazione ci concentreremo solamente sull'idea base preferendo la chiarezza all'efficienza.
Per fare ciò divideremo il problema in sottoproblemi più semplici:
- Trovare il massimo (o minimo) di una lista;
- Trovare l'indice dell'elemento massimo;
- Scambiare due elementi.
Ricerca di massimi di una lista¶
# definiamo una lista di numeri da ordinare
lista = [3, 21, 12, 45, 6, 1]
# determina lunghezza lista
num_elem = len(lista) # num_elem -> numero elementi
# stampa la lunghezza della lista
print(f"La lista ha {num_elem} numeri: {lista}.")
La lista ha 6 numeri: [3, 21, 12, 45, 6, 1].
Prima di iniziare a scrivere del codice prova a ragionare sul problema. Come fai a capire qual è il numero più grande della lista? Prova a scrivere a parole il procedimento e poi confrontalo con quello dei tuoi vicini. Avere chiaro in mente il problema e come risolverlo prima di scrivere codice aiuta a commettere meno errori (come in fisica; scrivere equazioni a caso difficilmente aiuta).
Suggerimento: prova a scrivere un semplice algoritmo che:
- Stampi la lista iniziale
- Trovi il numero maggiore e lo stampi
- Stampi il valore trovato e la sua posizione
È importante passare al punto n+1 solo dopo aver risolto il punto n. Il codice va scritto in maniera incrementale cercando di risolvere un sottoproblema alla volta!
# il tuo codice va qui
# il risultato che dovresti ottenere, per esempio, sarebbe
print("Lista iniziale: [3, 21, 12, 45, 6, 1].")
print("Il valore massimo trovato è 45 nella posizione 3.")
Lista iniziale: [3, 21, 12, 45, 6, 1]. Il valore massimo trovato è 45 nella posizione 3.
Modifica ora l'algoritmo per trovare invece il numero minore e stampare il valore trovato con la sua posizione all'interno della lista.
Ordinamento¶
Un modo per ordinare una lista di $N$ numeri consiste nel cercare il massimo e spostarlo nella posizione finale, a questo punto possiamo ripetere la procedura considerando solo i primi $N-1$ elementi, poi i primi $N-2$ e così via fino a quando non ci rimangono più elementi da ordinare. Per controllare che l'algoritmo funzioni, conviene stampare lo stato della lista ad ogni iterazione.
# il tuo codice va qui
# il risultato che dovresti ottenere, per esempio, sarebbe
print("Lista iniziale: [3,21,12,45,6,1].")
print("Al passo 0 abbiamo [3, 21, 12, 1, 6, 45].")
print("Al passo 1 abbiamo [3, 6, 12, 1, 21, 45].")
print("Al passo 2 abbiamo [3, 6, 1, 12, 21, 45].")
print("Al passo 3 abbiamo [3, 1, 6, 12, 21, 45].")
print("Al passo 4 abbiamo [1, 3, 6, 12, 21, 45].")
print("Al passo 5 abbiamo [1, 3, 6, 12, 21, 45].")
print("Lista finale: [1, 3, 6, 12, 21, 45].")
Lista iniziale: [3,21,12,45,6,1]. Al passo 0 abbiamo [3, 21, 12, 1, 6, 45]. Al passo 1 abbiamo [3, 6, 12, 1, 21, 45]. Al passo 2 abbiamo [3, 6, 1, 12, 21, 45]. Al passo 3 abbiamo [3, 1, 6, 12, 21, 45]. Al passo 4 abbiamo [1, 3, 6, 12, 21, 45]. Al passo 5 abbiamo [1, 3, 6, 12, 21, 45]. Lista finale: [1, 3, 6, 12, 21, 45].
Prova a modificare la lista di numeri iniziale e osserva come viene eseguito l'algoritmo. Nella versione corrente per una lista di num_elem numeri facciamo num_elem cicli, possiamo fare meglio?
Esercizi aggiuntivo: Bubble Sort¶
Prova a implementare l'algoritmo chiamato bubble sort per ordinare una lista di numeri. Lo schema è abbastanza simile a quanto abbiamo implementato sopra con la differenza che ora confrontiamo due elementi vicini e li scambiamo se quello a destra è minore di quello a sinistra. Questa procedura viene ripetuta scorrendo la lista da sinistra a destra più volte fino a quando non avvengono più scambi.

- Come primo passo prendi una lista di 6 numeri e scrivi un codice che scambia l'elemento nella posizione 3 con quella nella posizione 4
# il risultato che dovresti ottenere, per esempio, sarebbe
print("Lista iniziale: [3, 21, 12, 45, 6, 1].")
print("Lista finale: [3, 21, 12, 6, 45, 1].")
Lista iniziale: [3, 21, 12, 45, 6, 1]. Lista finale: [3, 21, 12, 6, 45, 1].
- Quante volte dobbiamo scorrere la lista al massimo per essere sicuri che non ci siano più scambi da effettuare?
- Implementa l'algoritmo bubble sort usando un ciclo for esterno per questo numero di iterazioni. Per verificare che l'algoritmo stia funzionando come dovrebbe, puoi ad esempio stampare la lista corrente ad ogni passo del primo ciclo
# il tuo codice va qui
print("Lista iniziale: [3, 21, 12, 45, 6, 1].")
print("Al passo 0 del primo ciclo abbiamo: [3, 21, 12, 45, 6, 1].")
print("Al passo 1 del primo ciclo abbiamo: [3, 12, 21, 45, 6, 1].")
print("Al passo 2 del primo ciclo abbiamo: [3, 12, 21, 45, 6, 1].")
print("Al passo 3 del primo ciclo abbiamo: [3, 12, 21, 6, 45, 1].")
print("Al passo 4 del primo ciclo abbiamo: [3, 12, 21, 6, 1, 45].")
print("Lista finale: [1, 3, 6, 12, 21, 45].")
Lista iniziale: [3, 21, 12, 45, 6, 1]. Al passo 0 del primo ciclo abbiamo: [3, 21, 12, 45, 6, 1]. Al passo 1 del primo ciclo abbiamo: [3, 12, 21, 45, 6, 1]. Al passo 2 del primo ciclo abbiamo: [3, 12, 21, 45, 6, 1]. Al passo 3 del primo ciclo abbiamo: [3, 12, 21, 6, 45, 1]. Al passo 4 del primo ciclo abbiamo: [3, 12, 21, 6, 1, 45]. Lista finale: [1, 3, 6, 12, 21, 45].
BONUS: Il vantaggio all'algoritmo bubble sort rispetto all'algoritmo che abbiamo implementato prima è che non necessariamente serve fare così tanti cicli: sappiamo che la lista è ordinata quando all'ultimo ciclo non abbiamo fatto più scambi. Prova a usare questa idea per implementare invece l'algoritmo bubble sort usando un ciclo while esterno.
# il tuo codice va qui
# con questo algoritmo il numero di cicli richiesti dipende dalla lista iniziale
## esempio 1
print("Lista iniziale: [3, 21, 12, 45, 6, 1].")
print("Al ciclo 0 abbiamo [3, 12, 21, 6, 1, 45].")
print("Al ciclo 1 abbiamo [3, 12, 6, 1, 21, 45].")
print("Al ciclo 2 abbiamo [3, 6, 1, 12, 21, 45].")
print("Al ciclo 3 abbiamo [3, 1, 6, 12, 21, 45].")
print("Al ciclo 4 abbiamo [1, 3, 6, 12, 21, 45].")
print("Al ciclo 5 abbiamo [1, 3, 6, 12, 21, 45].")
print("Lista finale dopo 6 cicli: [1, 3, 6, 12, 21, 45].")
## esempio 2
print("\n") # Stampa un `a capo` oltre a quello di default per un totale di 2 `a capo`.
print("Lista iniziale: [3, 21, 1, 45, 6, 12].")
print("Al ciclo 0 abbiamo [3, 1, 21, 6, 12, 45].")
print("Al ciclo 1 abbiamo [1, 3, 6, 12, 21, 45].")
print("Al ciclo 2 abbiamo [1, 3, 6, 12, 21, 45].")
print("Lista finale dopo 3 cicli: [1, 3, 6, 12, 21, 45].")
Lista iniziale: [3, 21, 12, 45, 6, 1]. Al ciclo 0 abbiamo [3, 12, 21, 6, 1, 45]. Al ciclo 1 abbiamo [3, 12, 6, 1, 21, 45]. Al ciclo 2 abbiamo [3, 6, 1, 12, 21, 45]. Al ciclo 3 abbiamo [3, 1, 6, 12, 21, 45]. Al ciclo 4 abbiamo [1, 3, 6, 12, 21, 45]. Al ciclo 5 abbiamo [1, 3, 6, 12, 21, 45]. Lista finale dopo 6 cicli: [1, 3, 6, 12, 21, 45]. Lista iniziale: [3, 21, 1, 45, 6, 12]. Al ciclo 0 abbiamo [3, 1, 21, 6, 12, 45]. Al ciclo 1 abbiamo [1, 3, 6, 12, 21, 45]. Al ciclo 2 abbiamo [1, 3, 6, 12, 21, 45]. Lista finale dopo 3 cicli: [1, 3, 6, 12, 21, 45].
BONUS: prova a misurare il tempo di esecuzione dei vari algoritmi che hai implementato in questa esercitazione per ordinare una lista e fai un grafico di questi tempi in funzione della dimensione della lista. Come ti aspetti che cambi il tempo in funzione della dimensione? Ottieni quello che pensavi?
NOTA¶
Visto che ordinare liste è un operazione abbastanza comune, non dovrebbe sorprendervi che in Python esiste già un implementazione di un algoritmo di sorting
# definiamo una lista di numeri da ordinare
lista = [3, 21, 12, 45, 6, 1]
print(f"Lista iniziale:\t{lista}.")
print(f"Lista ordinata:\t{sorted(lista)}.")
# notate che l'oggetto 'lista' non viene modificato da sorted
print(f"Lista iniziale:\t{lista}.")
# se vogliamo invece sovrascivere la lista con la versione ordinata possiamo fare
lista.sort()
print(f"Lista ordinata:\t{lista}.")
Lista iniziale: [3, 21, 12, 45, 6, 1]. Lista ordinata: [1, 3, 6, 12, 21, 45]. Lista iniziale: [3, 21, 12, 45, 6, 1]. Lista ordinata: [1, 3, 6, 12, 21, 45].
Esecizi extra¶
Il sito https://projecteuler.net/ fornisce una serie di esercizi di programmazione con base matematica molto interessanti da svolgere per fare pratica. Se avete tempo e voglia provate a risolverli, ma state attenti ad ordinare i problemi per numero di risoluzioni e a valutare se siete in grado di risolverli. La complessità aumenta molto velocemente.
Appendice - Perchè scrivere codice esplicativo¶
No man is an Iland, intire of it selfe; every man is a peece of the Continent, a part of the maine...
- John Donne
Ma che @#$!!%&! ÷ø fa sta funzione che ho scritto tre mesi fa??!?
- Guglielmo (che cerca di riutilizzare del codice che aveva scritto)
Nessuno scrive codice per se stesso, di conseguenza è di vitale importanza comunicare cosa si sta facendo e perchè nel modo più chiaro e diretto possibile (come gli articoli scientifici in pratica). Anche se pianificate di utilizzare solo voi il vostro script, difficilmente fra due settimane vi ricorderete le scelte che avevate fatto e i nomi che avete dato alle variabili. Per questo conviene sempre commentare le parti non autoesplicative del codice. Fidatevi, spendere questi 5 secondi ora vi farà risparmiare ore in futuro.

Esistono gare di codice pessimo ma funzionante pensate proprio per far capire quanto sia importante scrivere codice comprensibile.
Procediamo ora a scaricare ed eseguire uno dei vincitori dell'International Obfuscated Python Code Contest. Questo script stampa il codice stesso del programma e poi controlla quanto sia fedele alle linee guida per scrivere codice corretto.
!wget https://pyobfusc.com/submissions2023/1669075090/main.py
!echo "Mostrerò ora il contenuto del file \`main.py\` appena scaricato:"
!echo
!cat main.py
--2026-04-03 20:52:24-- https://pyobfusc.com/submissions2023/1669075090/main.py Resolving pyobfusc.com (pyobfusc.com)...
2a06:98c1:3120::, 2a06:98c1:3121::, 188.114.96.0, ... Connecting to pyobfusc.com (pyobfusc.com)|2a06:98c1:3120::|:443... connected. HTTP request sent, awaiting response...
200 OK
Length: 4337 (4,2K) [text/x-python] Saving to: ‘main.py.32’ main.py.32 0%[ ] 0 --.-KB/s main.py.32 100%[===================>] 4,24K --.-KB/s in 0s 2026-04-03 20:52:24 (18,7 MB/s) - ‘main.py.32’ saved [4337/4337]
Mostrerò ora il contenuto del file `main.py` appena scaricato:
"""ᐌ([ᐊ(ᐄ(ᐆ('print',ᐇ()),[ᐄ(ᐈ(ᐄ(ᐆ('open',ᐇ()), [ᐆ('__file__ ',ᐇ()),ᐃ('r'
)],ᐉ),'read',ᐇ()),ᐉ,ᐉ)],ᐉ)),ᐊ(ᐄ(ᐆ('print',ᐇ()),ᐉ,ᐉ)), ᐂ([ᐁ('conte' 'xtlib'),ᐁ(
'io'),ᐁ('operator'),ᐁ('time')]),Assign([ᐆ('f',ᐋ())],ᐄ(ᐈ( ᐆ('io',ᐇ()), 'StringIO',ᐇ
()),ᐉ,ᐉ)),With([withitem(ᐄ(ᐈ(ᐆ('contextlib',ᐇ()),'redirect' '_stdout',ᐇ( )),[ᐆ('f',ᐇ(
))],ᐉ))],[ᐂ( [ᐁ('this')])]) ,For(Tuple([ ᐆ('i',ᐋ()),ᐆ
('l',ᐋ())],ᐋ ()),ᐄ(ᐆ('enum' 'erate',ᐇ()) ,[ᐄ(ᐈ(ᐄ(ᐈ(ᐆ(
'f',ᐇ()),'g' 'etvalue',ᐇ()) ,ᐉ,ᐉ),'spli' 'tlines',ᐇ()
),ᐉ,ᐉ)],ᐉ),[ ᐎ(ᐒ(ᐏ(),ᐆ('i' ,ᐇ())),[ᐊ(ᐄ( ᐆ('print',ᐇ(
)),[ᐓ([ᐃ('H' 'ow does this' ' code do i' 'n terms of'
' >'),ᐑ(ᐄ(ᐈ( ᐆ('l',ᐇ()),'r' 'eplace',ᐇ() ),[ᐃ(','),ᐃ(
'<')],ᐉ),-1) ,ᐃ('?')])],ᐉ)) ,ᐐ()],[ᐎ(ᐒ(ᐏ (),ᐆ('l',ᐇ()
)),[ᐊ(ᐄ(ᐆ('' 'print',ᐇ()),ᐉ, ᐉ)),ᐐ()],ᐉ)] ),ᐊ(ᐄ(ᐆ('pr'
'int',ᐇ()),[ ᐍ(ᐅ(ᐆ('comment' ,ᐋ( )),ᐄ( ᐈ( Dict([ᐃ(11),
ᐃ(12),ᐃ(13), ᐃ(17),ᐃ(19)],[ᐃ( 'no errors were silenced'
),ᐃ('no err' 'ors were thrown'),ᐃ( 'everything is crystal'
' clear'),ᐃ('for this code, never would have been better') ,ᐃ('does not apply')]
),'get',ᐇ()),[ᐆ('i',ᐇ())],ᐉ)),ᐄ(ᐈ(ᐓ([ᐃ('\x1b[32m✔ '),ᐑ (ᐆ('l',ᐇ()),-1),ᐃ(
'\x1b[0m__#_'),ᐑ(ᐆ('comment',ᐇ()),-1),ᐃ('.')]),'re' 'place',ᐇ()),[ᐃ(
'_'),ᐃ(' ')],ᐉ),ᐓ([ᐃ('\x1b[91m✘ '),ᐑ(ᐆ('l',ᐇ()) ,-1)]))],ᐉ)),
ᐊ(ᐄ(ᐈ(ᐆ('time',ᐇ()),'sleep',ᐇ()),[ᐃ(0.3)],ᐉ ))],ᐉ),ᐊ(ᐄ(ᐆ
('print',ᐇ() ),[ᐃ('\x1b['
'0m')],ᐉ)),ᐊ (ᐄ(ᐆ('print'
,ᐇ()),[ᐃ('W' 'ell...')],ᐉ
) ),ᐊ(ᐄ(ᐈ(ᐆ( 'time',ᐇ()),
'sleep',ᐇ()) ,[ᐃ(3)],ᐉ)),
ᐊ(ᐄ(ᐆ('prin' 't',ᐇ()),[ᐃ(
'I tried.')] ,ᐉ))],ᐉ)""";
...;from ast import *;ᐁ,\
ᐂ,ᐃ,ᐅ,ᐄ,ᐆ,ᐈ, ᐇ,ᐌ,ᐉ,ᐋ,ᐊ,ᐍ\
,ᐐ, ᐎ,ᐏ,ᐒ,ᐓ, ᐑ = alias ,\
Import , Constant ,\
NamedExpr , Call, Name,\
Attribute , Load,Module\
,[], Store , Expr,IfExp,\
Continue,If, Not,UnaryOp\
,JoinedStr , FormattedValue
exec(unparse (fix_missing_locations
(eval(__doc__ ))))#Johannes Lippmann
# Eseguiamo ora il file.
!python3 main.py
"""ᐌ([ᐊ(ᐄ(ᐆ('print',ᐇ()),[ᐄ(ᐈ(ᐄ(ᐆ('open',ᐇ()), [ᐆ('__file__ ',ᐇ()),ᐃ('r'
)],ᐉ),'read',ᐇ()),ᐉ,ᐉ)],ᐉ)),ᐊ(ᐄ(ᐆ('print',ᐇ()),ᐉ,ᐉ)), ᐂ([ᐁ('conte' 'xtlib'),ᐁ(
'io'),ᐁ('operator'),ᐁ('time')]),Assign([ᐆ('f',ᐋ())],ᐄ(ᐈ( ᐆ('io',ᐇ()), 'StringIO',ᐇ
()),ᐉ,ᐉ)),With([withitem(ᐄ(ᐈ(ᐆ('contextlib',ᐇ()),'redirect' '_stdout',ᐇ( )),[ᐆ('f',ᐇ(
))],ᐉ))],[ᐂ( [ᐁ('this')])]) ,For(Tuple([ ᐆ('i',ᐋ()),ᐆ
('l',ᐋ())],ᐋ ()),ᐄ(ᐆ('enum' 'erate',ᐇ()) ,[ᐄ(ᐈ(ᐄ(ᐈ(ᐆ(
'f',ᐇ()),'g' 'etvalue',ᐇ()) ,ᐉ,ᐉ),'spli' 'tlines',ᐇ()
),ᐉ,ᐉ)],ᐉ),[ ᐎ(ᐒ(ᐏ(),ᐆ('i' ,ᐇ())),[ᐊ(ᐄ( ᐆ('print',ᐇ(
)),[ᐓ([ᐃ('H' 'ow does this' ' code do i' 'n terms of'
' >'),ᐑ(ᐄ(ᐈ( ᐆ('l',ᐇ()),'r' 'eplace',ᐇ() ),[ᐃ(','),ᐃ(
'<')],ᐉ),-1) ,ᐃ('?')])],ᐉ)) ,ᐐ()],[ᐎ(ᐒ(ᐏ (),ᐆ('l',ᐇ()
)),[ᐊ(ᐄ(ᐆ('' 'print',ᐇ()),ᐉ, ᐉ)),ᐐ()],ᐉ)] ),ᐊ(ᐄ(ᐆ('pr'
'int',ᐇ()),[ ᐍ(ᐅ(ᐆ('comment' ,ᐋ( )),ᐄ( ᐈ( Dict([ᐃ(11),
ᐃ(12),ᐃ(13), ᐃ(17),ᐃ(19)],[ᐃ( 'no errors were silenced'
),ᐃ('no err' 'ors were thrown'),ᐃ( 'everything is crystal'
' clear'),ᐃ('for this code, never would have been better') ,ᐃ('does not apply')]
),'get',ᐇ()),[ᐆ('i',ᐇ())],ᐉ)),ᐄ(ᐈ(ᐓ([ᐃ('\x1b[32m✔ '),ᐑ (ᐆ('l',ᐇ()),-1),ᐃ(
'\x1b[0m__#_'),ᐑ(ᐆ('comment',ᐇ()),-1),ᐃ('.')]),'re' 'place',ᐇ()),[ᐃ(
'_'),ᐃ(' ')],ᐉ),ᐓ([ᐃ('\x1b[91m✘ '),ᐑ(ᐆ('l',ᐇ()) ,-1)]))],ᐉ)),
ᐊ(ᐄ(ᐈ(ᐆ('time',ᐇ()),'sleep',ᐇ()),[ᐃ(0.3)],ᐉ ))],ᐉ),ᐊ(ᐄ(ᐆ
('print',ᐇ() ),[ᐃ('\x1b['
'0m')],ᐉ)),ᐊ (ᐄ(ᐆ('print'
,ᐇ()),[ᐃ('W' 'ell...')],ᐉ
) ),ᐊ(ᐄ(ᐈ(ᐆ( 'time',ᐇ()),
'sleep',ᐇ()) ,[ᐃ(3)],ᐉ)),
ᐊ(ᐄ(ᐆ('prin' 't',ᐇ()),[ᐃ(
'I tried.')] ,ᐉ))],ᐉ)""";
...;from ast import *;ᐁ,\
ᐂ,ᐃ,ᐅ,ᐄ,ᐆ,ᐈ, ᐇ,ᐌ,ᐉ,ᐋ,ᐊ,ᐍ\
,ᐐ, ᐎ,ᐏ,ᐒ,ᐓ, ᐑ = alias ,\
Import , Constant ,\
NamedExpr , Call, Name,\
Attribute , Load,Module\
,[], Store , Expr,IfExp,\
Continue,If, Not,UnaryOp\
,JoinedStr , FormattedValue
exec(unparse (fix_missing_locations
(eval(__doc__ ))))#Johannes Lippmann
How does this code do in terms of >The Zen of Python< by Tim Peters?
✘ Beautiful is better than ugly.
✘ Explicit is better than implicit.
✘ Simple is better than complex.
✘ Complex is better than complicated.
✘ Flat is better than nested.
✘ Sparse is better than dense.
✘ Readability counts.
✘ Special cases aren't special enough to break the rules.
✘ Although practicality beats purity.
✔ Errors should never pass silently. # no errors were silenced.
✔ Unless explicitly silenced. # no errors were thrown.
✔ In the face of ambiguity, refuse the temptation to guess. # everything is crystal clear.
✘ There should be one-- and preferably only one --obvious way to do it.
✘ Although that way may not be obvious at first unless you're Dutch.
✘ Now is better than never.
✔ Although never is often better than *right* now. # for this code, never would have been better.
✘ If the implementation is hard to explain, it's a bad idea.
✔ If the implementation is easy to explain, it may be a good idea. # does not apply.
✘ Namespaces are one honking great idea -- let's do more of those!
Well...
I tried.