Python Corso Fumera
Python Corso Fumera
1
Argomenti del corso
2
Organizzazione e funzionamento dei
calcolatori, sistema operativo
3
Evoluzione dei calcolatori: cenni storici
4
Evoluzione dei calcolatori: cenni storici
Le prime macchine calcolatrici meccaniche:
5
Evoluzione dei calcolatori: cenni storici
Anni ’30: primi calcolatori elettromeccanici in grado di svolgere
un solo tipo di calcolo, per esempio:
▶ costruzione di tabelle di
tiro per l’artiglieria
▶ risoluzione di sistemi di
equazioni lineari
6
Evoluzione dei calcolatori: cenni storici
Anni ’30: contributi dalla matematica
▶ analisi dei procedimenti di calcolo → algoritmi
▶ idea di macchina esecutrice automatica di algoritmi,
programmabile (calcolatore universale)
▶ organizzazione logica di un calcolatore programmabile
7
Evoluzione dei calcolatori: cenni storici
Algoritmo:
descrizione del procedimento per l’esecuzione di una da-
ta operazione, espressa in modo non ambiguo, in un lin-
guaggio comprensibile da un dato esecutore, in termini
di una sequenza finita di azioni, ciascuna delle quali sia
eseguibile dall’esecutore.
8
Evoluzione dei calcolatori: cenni storici
9
Nota
10
Architettura di Von Neumann
11
Memoria centrale
12
Unità centrale di elaborazione
13
Bus di sistema
▶ Bus dati
▶ Bus indirizzi
▶ Bus controlli
14
Architettura di un calcolatore: dettagli
15
Programmi
16
Insieme di istruzioni eseguibili dalla CPU
17
Esecuzione delle istruzioni
18
Avvio di un programma
19
Esecuzione delle istruzioni: fase di prelievo
1. AR ← PC il contenuto del PC viene copiato nell’AR
20
Esecuzione delle istruzioni: fase di prelievo
1. AR ← PC il contenuto del PC viene copiato nell’AR
READ
2. CU → M la CU chiede alla memoria un’operazione di lettura
21
Esecuzione delle istruzioni: fase di prelievo
1. AR ← PC il contenuto del PC viene copiato nell’AR
READ
2. CU → M la CU chiede alla memoria un’operazione di lettura
3. DR ← M[AR] il contenuto della cella il cui indirizzo si trova
nell’AR viene copiato del DR
22
Esecuzione delle istruzioni: fase di prelievo
1. AR ← PC il contenuto del PC viene copiato nell’AR
READ
2. CU → M la CU chiede alla memoria un’operazione di lettura
3. DR ← M[AR] il contenuto della cella il cui indirizzo si trova
nell’AR viene copiato del DR
4. CIR ← DR il contenuto del DR viene copiato nel CIR
23
Esecuzione delle istruzioni: fase di prelievo
1. AR ← PC il contenuto del PC viene copiato nell’AR
READ
2. CU → M la CU chiede alla memoria un’operazione di lettura
3. DR ← M[AR] il contenuto della cella il cui indirizzo si trova
nell’AR viene copiato del DR
4. CIR ← DR il contenuto del DR viene copiato nel CIR
5. PC ← PC +1 si memorizza nel PC l’indirizzo della prossima
istruzione
24
Evoluzione dei calcolatori
25
Estensioni dell’architettura di von Neumann
Due caratteristiche dell’architettura di von Neumann limitano la
velocità di esecuzione dei programmi:
▶ esecuzione sequenziale delle istruzioni di un programma,
anche se alcune di esse potrebbero essere eseguite in parallelo
▶ necessità di prelevare le istruzioni (e i dati da elaborare) dalla
memoria centrale: tale operazione richiede tempi superiori
rispetto all’esecuzione di un’istruzione logico-aritmetica
La velocità di esecuzione dei programmi dipende anche da altri
fattori, tra i quali:
▶ frequenza dell’orologio di sistema
▶ complessità delle istruzioni del linguaggio macchina
▶ ampiezza dei registri, del bus dati e del bus indirizzi
▶ capacità di memoria
▶ tempo d’accesso alla memoria
26
Estensioni dell’architettura di von Neumann
27
Il sistema di memoria
28
Gerarchia di memorie
29
Gerarchia di memorie
30
Memoria centrale
31
Memoria secondaria
▶ Obiettivi principali:
– non volatilità
– collegamento permanente con la CPU
– capacità elevata
– basso costo per bit
▶ Conseguenze:
– tecnologia: dischi magnetici (hard disk)
recentemente: tecnologia elettronica (solid state drive, SSD)
– tempo d’accesso molto elevato
32
Memoria secondaria: dischi magnetici
33
Memoria secondaria: dischi magnetici
34
Memoria terziaria
▶ Obiettivi principali:
– non volatilità
– facilità di rimozione dai calcolatori, per es. per il trasferimento
dati tra diversi calcolatori, e le copie di sicurezza dei dati
(backup)
– capacità elevata
– basso costo per bit
▶ Conseguenze:
– tecnologia: nastri magnetici, dischi magnetici, dischi ottici
(CD-ROM, CD-RW, DVD), tecnologia elettronica (per es.,
USB flash drive)
– tempo d’accesso molto elevato
35
Memoria cache
36
Modifiche all’unità di elaborazione
▶ Architetture pipeline
▶ CPU superscalari
▶ CPU multi-core
37
Modifiche all’unità di elaborazione: architettura pipeline
38
Sistemi operativi: cenni storici
▶ memoria centrale
▶ CPU
▶ dispositivi periferici
39
Sistemi operativi: cenni storici
40
Sistema operativo
41
Funzioni del sistema operativo
42
Organizzazione gerarchica del file system
43
Codifica binaria dell’informazione
44
Sommario
45
Introduzione
46
Codifica analogica
47
Esempio
48
Esempio
49
Codifica numerica
50
Esempio
Il codice Morse:
▶ mezzo fisico: impulso
elettrico, luminoso, ecc.
▶ grandezza fisica: durata
dell’impulso
▶ equivale a una codifica
numerica con k = 4:
– punto
– linea
– spazio tra due caratteri
– spazio tra due parole
52
Esempio
53
Esempio
Codifica dei suoni nei compact disc (CD-ROM):
▶ grandezza da codificare: segnale sonoro p(t) (pressione)
▶ quantizzazione: M = 216 = 65.536 intervalli
(nell’esempio in figura, M = 4)
▶ campionamento: 44.100 campioni/secondo
▶ mezzo fisico e codifica: presenza/assenza di fori (pit e land) di di-
mensione predefinita lungo una linea elicoidale sulla superficie del
CD-ROM
54
Codifica numerica
55
Codifica binaria
56
Sistemi di numerazione posizionali
57
Sistemi di numerazione posizionali
58
Esempi
Per chiarezza, la base può essere indicata (in lettere) come pedice.
59
Basi maggiori di dieci
60
Proprietà della rappresentazione posizionale
61
Proprietà della rappresentazione posizionale
3. Si consideri un valore x espresso in una base b da una sequenza di
p ≥ 0 cifre intere e q ≥ 0 cifre frazionarie tutte pari a b − 1 (per
es., in base dieci: 99 . . . 9}, |99 {z
| {z . . . 9}).
p cifre q cifre
Esempi:
63
Conversione dalla base due alla base dieci
64
Conversione dalla base dieci a un’altra base
= −(1
|
2 × 101−1 + 12 × 101−2})
× 1011 + 2 × 1010 + {z
in base tre
= ?
65
Conversione dalla base dieci a un’altra base
66
Esempio
67
Conversione della parte frazionaria
Il procedimento di conversione per la parte frazionaria non avrebbe
mai termine se N fosse irrazionale (per es., π), o se fosse periodico
nella base d’interesse (per es., 13 in base dieci). In questo caso si
dovrà terminare il procedimento dopo aver ottenuto un numero di
cifre frazionarie pari alla precisione desiderata, o non appena ci si
accorga che il numero è periodico.
68
Dimostrazione: parte intera
quozienti resti
N : b = N1 r0 (1) N = N1 × b + r0
N1 : b = N2 r1 (2) N1 = N2 × b + r1
N2 : b = N3 r2 (3) N2 = N3 × b + r2
...
Nn−2 : b = Nn−1 rn−2 (n − 1) Nn−2 = Nn−1 × b + rn−2
Nn−1 : b = 0 rn−1 (n) Nn−1 = rn−1
69
Dimostrazione: parte frazionaria
Sia 0, c−1 c−2 c−3 . . . la rappresentazione in base b di un numero
frazionario F :
70
Operazioni in basi diverse da dieci
riporti → 1 1
1 0 0 1 +
1 1 =
1 1 0 0
71
Codifica binaria dei numeri
72
Codifica binaria dei numeri
73
Codifica dei numeri naturali
{0, 1, 2, . . . , 2n − 1}
74
Esempio
valore codifica
in base dieci in base due
(con n = 3 cifre)
0 000 000
1 001 001
2 010 010
3 011 011
4 100 100
5 101 101
6 110 110
7 111 111
75
Esercizi
76
Soluzione
77
Codifica dei numeri interi: segno e valore
78
Codifica dei numeri interi: segno e valore
{0, 1, 2, . . . , 2n−1 − 1}
79
Codifica dei numeri interi: segno e valore
La codifica è la seguente:
▶ segno: positivo → 0, negativo → 1 (scelta convenzionale, è
possibile anche quella opposta)
▶ valore assoluto: come per i numeri naturali, si usa la
configurazione di n − 1 bit corrispondente alle n − 1 cifre
meno significative della rappresentazione in base due
80
Esempio
valore codifica
in base dieci in base due
(con n − 1 = 2 cifre)
−3 −11 111
−2 −10 110
−1 −01 101
−0 −00 100
+0 +00 000
+1 +01 001
+2 +10 010
+3 +11 011
81
Esercizi
82
Soluzione
83
Svantaggi della codifica in segno e valore
84
Codifica degli interi in complemento a due
85
Codifica degli interi in complemento a due
Nota: il bit più a sinistra risulta essere 0 nella codifica dei numeri
non negativi, mentre è 1 nella codifica dei numeri negativi. Il segno
non è però codificato separatamente dal suo valore assoluto: non è
infatti possibile ricavare il valore assoluto di un numero dai soli
n − 1 bit più a destra della sua codifica (come si può vedere
dall’esempio seguente).
86
Esempio
87
Proprietà della codifica in complemento a due
88
Esempi
89
Operazioni su valori codificati in complemento a due
90
Esempi: operazioni di addizione
2 + 1: −1 + (−3):
+2 → 0 1 0 + −1 → 1 1 1 +
+1 → 0 0 1 = −3 → 1 0 1 =
0 1 1 →3 1 1 0 0 → −4
2 + (−3): 2 + 3:
+2 → 0 1 0 + +2 → 0 1 0 +
−3 → 1 0 1 = +3 → 0 1 1 =
1 1 1 → −1 1 0 1 overflow
il terzo bit da destra del risultato
ha valore diverso rispetto a quello
degli operandi: ciò è dovuto al
fatto che il risultato, +5, non è
codificabile in complemento a due
con tre bit
91
Esempi: operazioni di sottrazione
2 − 1 = 2 + (−1): −2 − 3 = −2 + (−3):
+2 → 0 1 0 + −2 → 1 1 0 +
−1 → 1 1 1 = −3 → 1 0 1 =
1 0 0 1 → +1 1 0 1 1 overflow
(−5 non è codificabile in
complemento a due con tre bit)
1 − (−2) = 1 + 2:
+1 → 0 0 1 +
+2 → 0 1 0 =
0 1 1 → +3
92
Esercizi
93
Soluzione
94
Codifica dei numeri reali in virgola fissa
95
Codifica dei numeri reali in virgola fissa
96
Codifica dei numeri reali in virgola fissa
97
Esercizi
98
Soluzione
99
Soluzione
4. −7, 125dieci = −111, 001due . La parte frazionaria contiene un
numero finito di cifre significative (tre), e può quindi essere
codificata senza approssimazioni con almeno tre bit. La parte intera
contiene tre cifre significative, e deve essere codificata con almeno
tre bit. La codifica richiesta si ottiene quindi con sette bit: uno per
il segno, tre per la parte intera e tre per la parte frazionaria.
5. Il valore più piccolo diverso da zero che si può scrivere in base due
con q cifre nella parte frazionaria è:
0, |00 .{z
. . 01}
q cifre
|11 {z
. . . 1}, |11 {z
. . . 1}
p cifre q cifre
Pp−1
Tale valore corrisponde a i=−q 2i = 2p − 2−q .
100
Svantaggi della codifica in virgola fissa
Per poter codificare sia valori molto grandi che valori molto piccoli
in valore assoluto, è necessario un numero di bit molto elevato.
101
Notazione esponenziale
Esempio: −4739, 506 può essere riscritto (in infiniti modi) come
−4739, 506 × 100 , −47, 39506 × 102 , −473950600 × 10−5 , . . .
102
Notazione esponenziale
103
Notazione esponenziale
104
Codifica dei numeri reali in virgola mobile
105
Codifica dei numeri reali in virgola mobile
106
Codifica dei numeri reali in virgola mobile
107
Esercizi
108
Soluzione
1. +23,5078125dieci = +10111,1000001due
= +1,01111000001 × 10+100 . La codifica del segno è 0. La
codifica dell’esponente (in segno e valore con quattro bit) è
0100. La codifica della mantissa (le sette cifre frazionarie più
significative) è 0111100. La codifica di +23, 5078125dieci è
quindi 001000111100; in dettaglio: |{z} | {z }.
| {z } 0111100
0 0100
segno esponente mantissa
2. Il bit di segno (quello più a sinistra) è 0, quindi il numero è
positivo. La codifica dell’esponente (i successivi quattro bit) è
0010, che corrisponde a +10due . La codifica della mantissa
(i restanti sette bit) è 1101011, che corrisponde a
1, 1101011due . Il valore codificato è quindi 1, 1101011 × 10+10
(in base due) = +1, 8359375 × 2+2 (in base dieci).
109
Soluzione
110
Soluzione
111
Soluzione
5. (cont.)
Il valore più grande si ottiene combinando il valore più grande
di m e quello positivo e maggiore in valore assoluto di e:
P0
▶ m = 1, 11 . . . 1 =
| {z } i=−p 2i = 21 − 2−p ,
p cifre
Pq−2
▶ e = + 11 . . . 1 =
| {z } i=0 2i = 2q−1 − 1
q−1 cifre
Quindi:
q−1 −1
xmax = (2 − 2−p ) × 22
112
Codifica in virgola mobile: lo standard IEEE-754
La codifica sopra descritta presenta alcuni svantaggi. Per esempio,
richiede che la mantissa abbia parte intera pari a 1 e quindi non
consente di codificare senza approssimazioni il valore 0.
La codifica effettivamente usata nei calcolatori è una variante
definita nel 1985 dallo Institute of Electrical and Electronic
Engineers (nota come standard IEEE-754):
▶ prevede diversi formati:
– n = 32, p = 23, q = 8
– n = 64, p = 52, q = 11
– n = 128, p = 112, q = 15
▶ codifica dell’esponente in eccesso a 2q−1
▶ codifica dello zero: bit di mantissa ed esponente tutti pari a 0
▶ alcune configurazioni degli n bit sono usate per codificare:
– il valore “not a number ” (NaN), per es. il risultato di 0/0
– i valori +∞ e −∞
– alcuni valori con mantissa avente parte intera nulla
113
Codifica dei numeri: considerazioni finali
114
Algoritmi: formulazione, rappresentazione,
proprietà
115
Sommario
▶ Algoritmi e programmi
▶ Approccio alla formulazione di algoritmi e alla
programmazione
▶ Rappresentazione di algoritmi: diagrammi di flusso
▶ Proprietà degli algoritmi: correttezza ed efficienza
116
Algoritmi e programmi
117
Approccio alla formulazione di algoritmi e alla
programmazione
118
Formulazione di algoritmi
119
Rappresentazione di algoritmi
120
Diagrammi di flusso
121
Diagrammi di flusso
122
Blocchi di inizio e conclusione di un algoritmo
INIZIO
FINE
INIZIO
▶ ogni diagramma di flusso deve iniziare con il blocco e
FINE
terminare con il blocco
▶ possono esserci più occorrenze del blocco FINE, al termine di
diverse ramificazioni di un diagramma di flusso
corrispondenti ai blocchi di selezione
▶ il blocco INIZIO è l’unico a non avere una freccia entrante, e
il blocco FINE è l’unico a non averne una uscente
123
Blocchi di ingresso/uscita
Sono rappresentati da parallelogrammi, e rappresentano le
operazioni di:
▶ “acquisizione” dei valori da elaborare (i valori “d’ingresso”
dell’algoritmo) e la loro associazione a identificatori simbolici:
di norma si usa il termine acquisire seguito dal nome
dell’identificatore
▶ “stampa” dei risultati dell’algoritmo: di norma si usa il
termine stampare seguito da un’espressione il cui valore dovrà
essere stampato, o da un messaggio (scritto tra doppi apici)
Alcuni esempi:
124
Blocchi di ingresso/uscita
125
Blocco di elaborazione
identificatore ← espressione
Alcuni esempi:
𝑥←1 𝑝 ← 𝑥 + 3 ⁄2 𝑐←𝑐+1
126
Blocco di elaborazione
127
Blocco di selezione
È rappresentato da un rombo con due frecce uscenti.
Esprime la scelta tra due sequenze alternative di operazioni, una
sola delle quali verrà eseguita, in base al verificarsi o meno di una
data condizione.
La condizione consiste di norma in un confronto tra due espres-
sioni aritmetiche per mezzo degli operatori <, ≤, =, ̸=, ≥, >.
Le frecce uscenti dal blocco di selezione sono associate a
“etichette” indicanti il verificarsi (Vero) o meno (Falso) della
condizione (per esempio, i simboli V e F).
Un esempio:
F V
𝑥<0
128
Formulazione di algoritmi
Quando si formula un algoritmo, o se ne vuole verificare la
correttezza, o se ne vuole comprendere il funzionamento (nel caso
in cui sia stato formulato da altri), è spesso utile provare a
eseguirlo (preferibilmente usando carta e matita) per alcuni
possibili valori dei dati d’ingresso.
A questo scopo è conveniente tener traccia del valore corrente
associato a ciascuno degli identificatori simbolici:
▶ dopo ogni operazione di acquisizione o di elaborazione si
aggiornerà il valore dell’identificatore corrispondente
▶ se all’identificatore coinvolto era già associato un valore,
questo sarà sostituito dal nuovo valore
Quando l’algoritmo sarà tradotto in un programma ed eseguito da
un calcolatore, i riquadri (ovvero gli identificatori)
corrisponderanno alle celle di memoria nelle quali verranno
memorizzati i dati d’ingresso e i risultati intermedi e finali.
129
Esempi
130
Esempio 1: calcolo di un polinomio
INIZIO
L’algoritmo può essere descritto
Calcolaredi
come una semplice sequenza il valore del
polinomio $3x^2 - 2x + 1$ acquisire !
blocchi di ingresso/uscita
per e
undi
dato valore di $x$.
elaborazione: " ←3%!%!−2%!+1
stampare "
FINE
131
Esempio 2: valore assoluto
132
FINE
stampare ,
FINE
133
Esempio 2: valore assoluto
stampare 𝑥
FINE
134
Esempio 3: equazioni di primo grado
acquisire 𝑎, 𝑏
V F
𝑎=0
stampare 𝑥 ← − 𝑏 ⁄𝑎
"equazione indefinita"
stampare 𝑥
FINE
135
Esempio 4: equazioni di secondo grado
136
Esempio 4: equazioni di secondo grado
INIZIO
acquisire 𝑎, 𝑏, 𝑐
V F
𝑎=0
stampare
𝑑𝑒𝑙𝑡𝑎 ← 𝑏 , 𝑏 − 4 , 𝑎 , 𝑐
"l'equazione non è
di secondo grado"
V F
𝑑𝑒𝑙𝑡𝑎 < 0
stampare
"le radici sono 𝑥1 ← −𝑏 + 𝑑𝑒𝑙𝑡𝑎 4 2 , 𝑎
complesse"
𝑥2 ← −𝑏 − 𝑑𝑒𝑙𝑡𝑎 4 2 , 𝑎
stampare
𝑥1, 𝑥2
FINE
137
Esempio 5: sommatoria
acquisire
𝑎, 𝑏, 𝑐, 𝑑, 𝑒
𝑠𝑜𝑚𝑚𝑎 ← 𝑎 + 𝑏 + 𝑐 + 𝑑 + 𝑒
stampare 𝑠𝑜𝑚𝑚𝑎
FINE
138
Esempio 5: sommatoria
139
Esempio 5: sommatoria
INIZIO INIZIO
acquisire ! acquisire !
"#$$! ← ! "#$$! ← !
acquisire ! '←1
"#$$! ← "#$$! + !
F V
' <5
acquisire !
acquis
"#$$! ← "#$$! + !
"#$$! ← "
acquisire !
'←'
"#$$! ← "#$$! + !
acquisire !
FINE
stampare "#$$!
FINE
140
Esempio 5: sommatoria
141
Esempio 5: sommatoria
INIZIO
acquisire 𝑎
𝑠𝑜𝑚𝑚𝑎 ← 𝑎
𝑐←1
F V
𝑐<5
acquisire 𝑎
𝑠𝑜𝑚𝑚𝑎 ← 𝑠𝑜𝑚𝑚𝑎 + 𝑎
𝑐 ←𝑐+1
stampare 𝑠𝑜𝑚𝑚𝑎
FINE
142
Esempio 5: sommatoria
143
Esempio 6: sommatoria
144
Esempio 6: sommatoria
INIZIO
acquisire 𝑛
acquisire 𝑎
𝑠𝑜𝑚𝑚𝑎 ← 𝑎
𝑐←1
F V
𝑐<𝑛
acquisire 𝑎
𝑠𝑜𝑚𝑚𝑎 ← 𝑠𝑜𝑚𝑚𝑎 + 𝑎
𝑐 ← 𝑐 +1
stampare 𝑠𝑜𝑚𝑚𝑎
FINE
145
Esempio 7: fattoriale
n! = 1 × 2 × 3 × . . . × (n − 1) × n, se n > 0
0! = 1
Anche in questo caso è necessario uno schema iterativo (si veda la pagina
seguente). I valori da moltiplicare vengono facilmente calcolati (e
associati all’identificatore c) in funzione del valore di n.
Si noti che l’assegnamento iniziale f ← 1 corrisponde al fattoriale dei
primi due numeri naturali (0! e 1!): per questo motivo è sufficiente
proseguire il calcolo (se n > 1) a partire dal terzo fattore, cioè 2.
146
Esempio 7: fattoriale
INIZIO
acquisire 𝑛
𝑓←1
𝑐←2
F V
𝑐≤𝑛
𝑓←𝑓&𝑐
𝑐 ← 𝑐 +1
stampare 𝑓
FINE
147
Esempio 8: serie armonica
148
Esempio 8: serie armonica
INIZIO
acquisire 𝑛
𝑠𝑒𝑟𝑖𝑒 ← 1
𝑘←2
F V
𝑘≤𝑛
𝑘 ← 𝑘 +1
stampare 𝑠𝑒𝑟𝑖𝑒
FINE
149
Esempio 9: massimo di un insieme di numeri
150
Esempio 9: massimo di un insieme di numeri
INIZIO
acquisire 𝑛
acquisire 𝑥
𝑚𝑎𝑥 ← 𝑥
𝑐←1
F V
𝑐<𝑛
acquisire 𝑥
F V
𝑥 > 𝑚𝑎𝑥
𝑚𝑎𝑥 ← 𝑥
𝑐 ← 𝑐 +1
stampare 𝑚𝑎𝑥
FINE
151
Esempio 9: massimo di un insieme di numeri
152
Esempio 10: massimo comun divisore
153
Esempio 10: massimo comun divisore
154
Esempio 10: massimo comun divisore
INIZIO
acquisire 𝑚, 𝑛
𝑚 <𝑛
𝑚𝑖𝑛 ← 𝑛 𝑚𝑖𝑛 ← 𝑚
𝑑 ← 𝑚𝑖𝑛
F 𝑚 mod 𝑑 ≠ 0 V
oppure
𝑛 𝑚𝑜𝑑 𝑑 ≠ 0
𝑑 ←𝑑−1
stampare 𝑑
FINE
155
Esempio 10: massimo comun divisore
156
Esercizio: minimo comune multiplo
157
Il teorema di Böhm-Jacopini
158
Osservazione sugli schemi iterativi
Nei diagrammi di flusso gli schemi iterativi possono avere diverse
strutture, come per esempio quelle mostrate qui sotto, dove i
riquadri tratteggiati indicano una qualsiasi sequenza di blocchi:
F V F V
... ...
F V
...
F V
...
160
Formulazione di algoritmi: problemi e istanze
161
Osservazioni finali
162
Proprietà degli algoritmi: correttezza
Un algoritmo è corretto se produce il risultato desiderato per ogni
possibile istanza del problema.
Il metodo più “semplice” per verificare la correttezza di un dato
algoritmo consiste nell’eseguirlo per tutte le istanze del problema.
Tuttavia nella pratica questo è spesso impossibile:
▶ il numero d’istanze può molto grande (in teoria può anche
essere infinito)
▶ un algoritmo non corretto potrebbe non terminare mai per
qualche istanza del problema
Di norma la correttezza deve quindi essere dimostrata con
procedimenti analoghi alle dimostrazioni dei teoremi matematici.
La non correttezza può invece essere dimostrata individuando
anche un singolo controesempio, cioè un’istanza per la quale
l’algoritmo non produce il risultato desiderato.
163
Proprietà degli algoritmi: efficienza
164
Linguaggi e ambienti di programmazione
165
Il linguaggio macchina
166
Il linguaggio macchina
167
Il linguaggio macchina
Per semplificare l’attività di programmazione, a partire dagli anni ’50
sono stati introdotti diversi linguaggi di alto livello, che consentono di
rappresentare gli algoritmi in una forma più vicina al linguaggio naturale
rispetto al linguaggio macchina.
I linguaggi di alto livello possiedono in particolare le seguenti
caratteristiche che li differenziano dal linguaggio macchina:
▶ non fanno esplicito riferimento agli indirizzi delle celle di memoria,
ma usano nomi simbolici (detti variabili)
▶ indicano le istruzioni con termini del linguaggio naturale (per
esempio, if, while, for)
▶ consentono di rappresentare espressioni aritmetiche composte da più
operazioni elementari, con una notazione simile a quella usata in
matematica
▶ consentono di rappresentare dati composti da aggregazioni di valori
più elementari (per esempio, sequenze di caratteri e vettori di
numeri)
168
Linguaggi di alto livello
Esempi:
▶ Fortran ▶ Pascal
(FORmula TRANslator ) ▶ SmallTalk
▶ Lisp (LISt Processor ) ▶ C++
▶ Cobol (COmmon Business ▶ Java
Oriented Language) ▶ Python
▶ C ▶ Matlab
▶ Basic ▶ ...
169
Linguaggi di alto livello
170
Linguaggi di alto livello
171
Traduzione in linguaggio macchina
172
Linguaggi compilati e interpretati
173
Ambienti di programmazione
174
Il linguaggio Python
175
Sommario
176
Introduzione al linguaggio Python
177
Cenni storici e motivazioni
▶ Il linguaggio Python:
– linguaggio di alto livello, interpretato, open source
– ideato nel 1989 dall’informatico olandese Guido Van Rossum
(https://www.python.org/~guido/)
– ideato originariamente come linguaggio di scripting, poi
evolutosi come linguaggio completo
– punto di forza: facilità nella scrittura dei programmi
– sono disponibili diversi ambienti di programmazione
▶ Perché si usa Python in questo corso:
– sintassi minimale, facile da imparare
– possiede gli elementi di base comuni a tutti i linguaggi di alto
livello: fornisce quindi le basi per l’apprendimento, anche
autonomo, di altri linguaggi
178
Documentazione e risorse
179
L’ambiente di programmazione IDLE
180
L’ambiente di programmazione IDLE
181
Elementi principali del linguaggio Python
182
Istruzioni, espressioni, variabili
Come per la maggior parte dei linguaggi di alto livello, un programma
Python è composto da una sequenza di istruzioni.
Tre sono le istruzioni fondamentali:
▶ istruzione di assegnamento
▶ istruzione condizionale
▶ istruzione iterativa
Ognuna di tali istruzioni contiene espressioni che producono valori da
elaborare (per esempio, espressioni aritmetiche).
In particolare, l’istruzione di assegnamento consente di memorizzare i dati
da elaborare e i risultati nelle celle di memoria del calcolatore, alle quali si
fa riferimento attraverso nomi simbolici detti variabili.
Sono inoltre disponibili funzioni per l’acquisizione dei dati da elaborare,
attraverso dispositivi periferici d’ingresso (input), quali la tastiera e la
memoria secondaria, e per l’invio di dati ai dispositivi d’uscita (output),
come la memoria secondaria e lo schermo. (Il concetto di funzione nei
linguaggi di alto livello e in Python verrà spiegato più avanti.)
183
Scrittura ed esecuzione di programmi
184
Scrittura ed esecuzione di programmi
185
Scrittura ed esecuzione di programmi
186
Scrittura ed esecuzione di programmi
187
Scrittura ed esecuzione di programmi
L’esecuzione di un programma causa il “riavvio” (restart) della shell (il
significato del “riavvio” diventerà chiaro più avanti). I risultati delle
elaborazioni verranno poi mostrati sulla stessa shell.
Per esempio, questo è il risultato dell’esecuzione del programma
precedente (si noti il messaggio che indica il “riavvio” della shell):
188
Scrittura ed esecuzione di programmi
189
Istruzioni, espressioni, variabili
190
Un suggerimento per lo studio
191
Variabili e istruzione di assegnamento
192
Variabili e istruzione di assegnamento: sintassi
193
Variabili e istruzione di assegnamento: sintassi
194
Variabili e istruzione di assegnamento: semantica
195
Espressioni e tipi di dato
196
Espressioni aritmetiche
La più semplice espressione aritmetica è un singolo numero.
I numeri vengono rappresentati nelle istruzioni Python in base
dieci, con diverse possibili notazioni.
Python distingue tra due tipi di dato numerici:
▶ numeri interi, codificati nei calcolatori in complemento a due;
esempi: 12, -9
▶ numeri frazionari (floating point), codificati in virgola mobile,
e rappresentati nei programmi:
– come parte intera e frazionaria, separate da un punto
(notazione anglosassone); esempi: 3.14, -45.2, 1.0
– in notazione esponenziale, m × b e , con base b pari a dieci, ed
esponente introdotto dal carattere E oppure e; esempi:
1.99E33 (1, 99 × 1033 ), -42.3e-4 (−42, 3 × 10−4 ), 2E3 (2 × 103 )
NOTA: i numeri espressi in notazione esponenziale sono
sempre considerati numeri frazionari
197
Espressioni aritmetiche
simbolo operatore
+ somma
- sottrazione
* moltiplicazione
/ divisione
// divisione (quoziente intero)
% modulo (resto di una divisione)
** elevamento a potenza
198
Espressioni aritmetiche: esempi
199
Espressioni aritmetiche: osservazioni
200
Espressioni aritmetiche: osservazioni
Esempi:
6//2 → 3
6.0//2 → 3.0
2//5 → 0
2//5.0 → 0.0
-2//3 → -1
201
Espressioni che elaborano stringhe
202
Espressioni che elaborano stringhe
203
Espressioni contenenti nomi di variabili
Come caso particolare, anche il nome di una variabile alla quale sia
già stato assegnato un valore costituisce un’espressione.
204
Espressioni contenenti nomi di variabili
Da quanto detto sopra segue che se a una variabile è già stato assegnato
un valore, il suo nome potrà essere usato come operando all’interno di
un’espressione. Quando l’interprete valuterà tale espressione, sostituirà al
nome della variabile il valore a essa associato. Esempi:
▶ eseguendo le istruzioni
raggio = 2
circonferenza = raggio*6.28
alla variabile circonferenza sarà assegnato il valore 12.56
▶ eseguendo le istruzioni
parola1 = "mappa"
parola2 = parola1 + "mondo"
alla variabile parola2 sarà assegnato il valore "mappamondo"
205
Espressioni contenenti nomi di variabili
206
Espressioni contenenti nomi di variabili
Per esempio, assumendo che alla variabile h non sia ancora stato
assegnato nessun valore, l’esecuzione dell’istruzione
x = h + 1
causerà la terminazione del programma da parte dell’interprete e la
stampa di un messaggio d’errore nella shell, come si vedrà più
avanti.
207
Programmi e istruzioni di assegnamento
208
Programmi e istruzioni di assegnamento
209
La funzione print
210
La funzione print: sintassi e semantica
Sintassi: print(espressione)
▶ non devono esserci spazi prima del simbolo print
▶ espressione deve essere una qualsiasi espressione valida del
linguaggio Python
211
La funzione print: esempi
x = 5
p = "Mappa"
print(-3.2)
print(2 + 2)
print("Ciao")
print(x)
print((x + 1)/4.0)
print("Il valore associato a x è", x)
print(p + "mondo")
212
La funzione print: esempi
213
Caratteri speciali nelle stringhe
214
Caratteri speciali nelle stringhe: esempi
215
Esercizi
216
La funzione input
217
La funzione input: sintassi e semantica
218
La funzione input come espressione
219
La funzione input: esempio
220
La funzione input: esempio
221
Acquisizione di dati tramite input: limitazioni
222
Acquisizione di dati tramite input: limitazioni
223
La funzione eval
224
La funzione eval: esempio
225
La funzione eval: esempio
226
La funzione eval
227
Errori di sintassi
228
Errori di sintassi
229
Errori di sintassi
Non è consentito inserire spazi all’inizio di una qualsiasi riga di un
programma (con le eccezioni descritte più avanti).
Se ciò avviene, quando il programma verrà eseguito all’interno
dell’ambiente IDLE, comparirà un messaggio d’errore nella finestra
dell’editor, come mostrato in questo esempio:
230
Errori di sintassi
231
Uso interattivo della shell
232
Uso interattivo della shell
L’uso interattivo della shell è utile per diversi scopi, per esempio:
▶ verificare l’esito dell’esecuzione di una singola istruzione, o il
valore di una singola espressione
▶ visualizzare il valore associato a una variabile (si ricordi che i
nomi delle variabili sono espressioni)
▶ usare la shell di Python come una calcolatrice con memoria
233
Uso interattivo della shell: esempi
La figura che segue mostra un esempio di sessione interattiva con
la shell. Le righe che iniziano con il prompt (>>>) contengono
espressioni e istruzioni scritte dall’utente; le altre (di colore blu)
contengono i valori delle espressioni e quelli stampati mediante la
funzione print.
234
Uso delle funzioni print e input
235
Uso delle funzioni print e input: esempio
236
Osservazioni sull’esecuzione dei programmi
237
Osservazioni sull’esecuzione dei programmi: esempio
238
Osservazioni sull’esecuzione dei programmi
239
Osservazioni sull’esecuzione dei programmi: esempio
240
Esercizi di riepilogo
241
Esercizi di riepilogo
242
Esercizi di riepilogo
243
Commenti all’interno dei programmi
244
Primi esempi di programmi Python
245
Primi esempi di programmi Python
246
Primi esempi di programmi Python
247
L’istruzione condizionale
Esempi:
▶ nel calcolo del valore assoluto di un numero, il risultato
dipende dal segno di tale numero
▶ la soluzione di un’equazione di primo grado, ax + b = 0
è data da − ba , se a ̸= 0, altrimenti non è definita o non è
unica
248
L’istruzione condizionale
249
Espressioni condizionali: sintassi
250
Espressioni condizionali: operatori di confronto
simbolo significato
== “uguale a” (notare il doppio simbolo
di uguaglianza, per evitare ambiguità
con l’istruzione di assegnamento)
!= “diverso da”
< “minore di”
<= “minore o uguale a”
> “maggiore di”
>= “maggiore o uguale a”
251
Espressioni condizionali: esempi
252
Espressioni condizionali: semantica
253
Uso delle espressioni condizionali
Oltre che all’interno delle istruzioni condizionali (e iterative, come si
vedrà più avanti), le espressioni condizionali possono essere usate
all’interno di un programma Python come espressioni a sé stanti:
▶ il loro valore può essere assegnato a una variabile; per esempio, se
alla variabile x è già stato assegnato un valore numerico:
risultato = (x > 0)
▶ il loro valore può essere stampato nella finestra della shell con
l’istruzione print, per esempio:
print(x > 0)
▶ possono essere scritte nella shell, nella quale verrà mostrato il loro
valore (True oppure False)
▶ gli stessi simboli True e False possono essere assegnati a variabili,
in quanto costituiscono espressioni, analogamente ai numeri e alle
stringhe; per esempio:
trovato = True
254
Espressioni condizionali: esempi
255
Espressioni condizionali: esempi
256
Espressioni condizionali: esempi
257
Espressioni condizionali: esempi
258
Espressioni condizionali composte
259
Espressioni condizionali composte: sintassi
Sintassi:
▶ espr-cond1 and espr-cond2
▶ espr-cond1 or espr-cond2
▶ not espr-cond
dove:
▶ espr-cond1 , espr-cond2 e espr-cond sono espressioni
condizionali semplici, o a loro volta espressioni condizionali
composte
▶ i simboli and, or e not sono detti operatori logici, e
corrispondono rispettivamente ai connettivi logici del
linguaggio naturale “e”, “oppure”, “non”
▶ è possibile usare le parentesi tonde per definire l’ordine di
precedenza degli operatori logici
260
Espressioni condizionali composte: semantica
261
Espressioni condizionali composte: esempi
262
Espressioni condizionali: esempi
263
L’istruzione condizionale
264
L’istruzione condizionale
VERO FALSO
condizione
sequenza sequenza
d'istruzioni 1 d'istruzioni 2
...
265
L’istruzione condizionale: sintassi
if espr-cond:
sequenza di istruzioni 1
else:
sequenza di istruzioni 2
▶ le parole-chiave if e else devono essere scritte senza rientri
▶ espr-cond è un’espressione condizionale
▶ sequenza di istruzioni 1 e sequenza di istruzioni 2 sono due
sequenze di una o più istruzioni qualsiasi
▶ ciascuna di tali istruzioni deve essere scritta in una riga
distinta, con un rientro di almeno un carattere; il rientro
deve essere identico per tutte le istruzioni
266
L’istruzione condizionale: semantica
Si noti che solo una delle due sequenze di istruzioni viene eseguita.
267
L’istruzione condizionale: esempio
Si assuma che alla variabile x sia già stato assegnato un valore:
if x > 0:
print("La condizione è vera.")
z = x + 1
else:
print("La condizione è falsa.")
Se il valore associato alla variabile x (nel momento in cui l’istruzione
condizionale viene eseguita) è un numero positivo, viene prima mostrato
(nella shell) il messaggio
La condizione è vera.
poi viene assegnato alla variabile z il valore dell’espressione x + 1.
Altrimenti (se l’espressione x > 0 è falsa) viene mostrato il messaggio
La condizione è falsa.
Sintassi:
if espr-cond:
sequenza di istruzioni
269
L’istruzione condizionale: una variante
VERO
condizione
sequenza FALSO
d'istruzioni
270
L’istruzione condizionale: esempio
271
Ancora sulla sintassi dell’istruzione condizionale
I rientri (ovvero gli spazi all’inizio di una riga) sono l’unico elemento
sintattico che indica quali istruzioni fanno parte di un’istruzione
condizionale. Le istruzioni che seguono un’istruzione condizionale (senza
farne parte) devono quindi essere scritte senza rientri rispetto a essa.
Come esempio, si considerino le due sequenze di istruzioni:
if x > 0: if x > 0:
print("A") print("A")
print("B") print("B")
Nella sequenza a sinistra le due chiamate di print sono scritte con un
rientro rispetto a if: questo significa che fanno entrambe parte
dell’istruzione condizionale, e quindi verranno eseguite solo se la
condizione x > 0 sarà vera.
Nella sequenza a destra solo la prima chiamata di print dopo if è
scritta con un rientro, e quindi solo essa fa parte dell’istruzione
condizionale; la seconda verrà invece eseguita dopo l’istruzione
condizionale, indipendentemente dal valore della condizione x > 0.
272
Istruzione condizionale: esercizio
if x > 0:
print("A")
y = 2
if y <= 2:
print("B")
print("C")
else:
print("D")
print("E")
273
Istruzioni condizionali nidificate
274
Istruzioni condizionali nidificate: esempio
In questo esempio l’istruzione condizionale
if y == 1: ...
è nidificata all’interno di un’altra istruzione condizionale:
if x > 0: ...
(si assume che alle variabili x e y sia già stato assegnato un valore).
Si noti che entrambe contengono anche la parte else.
if x > 0:
print("A")
if y == 1:
print("B")
else:
print("C")
print("D")
else:
print("E")
275
Istruzioni condizionali nidificate: esempio
276
Istruzioni condizionali nidificate: esempio
if x > 0:
print("A")
if y == 1:
print("B")
else:
print("C")
print("D")
else:
print("E")
277
Istruzioni condizionali nidificate: esempio
278
Istruzioni condizionali nidificate: esempio
In conclusione:
▶ se x > 0 è vera:
– prima viene stampato A
– poi, se y == 1 è vera viene stampato B, altrimenti viene
stampato C
– poi viene stampato D
▶ se invece x > 0 è falsa, viene solo stampato E
279
Istruzioni condizionali nidificate: esercizio
280
Esempi di programmi contenenti istruzioni condizionali
281
Esempi di programmi contenenti istruzioni condizionali
282
Esempi di programmi contenenti istruzioni condizionali
283
Esempi di programmi contenenti istruzioni condizionali
284
Esempi di programmi contenenti istruzioni condizionali
285
Esempi di programmi contenenti istruzioni condizionali
Un’altra variante, che determina se le radici siano reali e distinte,
reali e coincidenti, oppure complesse coniugate. In questo esempio
vengono usate tre istruzioni condizionali in sequenza (non nidifica-
te), corrispondenti a tre condizioni mutuamente esclusive: quindi
una sola delle istruzioni corrispondenti verrà eseguita.
File: 8_eq_secondo_grado_3.py
286
Esempi di programmi contenenti istruzioni condizionali
287
Esempi di programmi contenenti istruzioni condizionali
288
Esempi di programmi contenenti istruzioni condizionali
File: 10_triangoli_1.py
289
Esempi di programmi contenenti istruzioni condizionali
290
Esempi di programmi contenenti istruzioni condizionali
File: 11_triangoli_2.py
291
L’istruzione condizionale: una seconda variante
292
L’istruzione condizionale: una seconda variante
293
Esempio
Il programma visto in precedenza può essere riscritto come segue (si veda
il file 11_triangoli_3.py):
294
L’istruzione iterativa
295
L’istruzione iterativa: sintassi
while espr-cond:
sequenza di istruzioni
296
L’istruzione iterativa: semantica
297
L’istruzione iterativa: semantica
VERO
condizione
sequenza FALSO
d'istruzioni
...
298
L’istruzione iterativa: esempi
while 1 > 2:
print("Python")
x = 7
299
L’istruzione iterativa: esempi
while 1 < 2:
print("Python")
x = 7
300
L’istruzione iterativa: esempi
k = 1
while k <= 3:
print("Buongiorno")
k = k + 1
301
L’istruzione iterativa: esempi
302
L’istruzione iterativa: esempi
Di seguito si schematizza l’esecuzione del programma seguendo il
metodo suggerito sopra:
▶ a sinistra si riporta il programma e si evidenzia (con il colore
magenta) l’istruzione in esecuzione
▶ al centro:
– in alto si mostra il valore associato alla variabile k dopo
l’esecuzione dell’istruzione evidenziata
– in basso si mostra (in blu) ciò che compare nella shell
k = 1 k
while k <= 3:
print("Buongiorno")
k = k + 1
303
L’istruzione iterativa: esempi
Il programma è composto da due istruzioni: un’istruzione di
assegnamento e un’istruzione iterativa (che a sua volta contiene
altre istruzioni).
La prima istruzione che viene eseguita è l’assegnamento k = 1:
k = 1 k 1
while k <= 3:
print("Buongiorno")
k = k + 1
304
L’istruzione iterativa: esempi
Inizia quindi l’esecuzione dell’istruzione iterativa. Viene prima di
tutto valutata l’espressione condizionale, che risulta vera; di
conseguenza, verranno successivamente eseguite le istruzioni
all’interno dell’istruzione iterativa.
k = 1 k 1
while k <= 3:
print("Buongiorno")
k = k + 1
305
L’istruzione iterativa: esempi
La prima di tali istruzioni (più precisamente, si tratta di una
chiamata di funzione) causa la stampa della stringa Buongiorno
nella shell:
k = 1 k 1
while k <= 3:
print("Buongiorno") Buongiorno
k = k + 1
306
L’istruzione iterativa: esempi
La seconda istruzione modifica il valore associato alla variabile k,
incrementando di una unità il valore attuale:
k = 1 k 2
while k <= 3:
print("Buongiorno") Buongiorno
k = k + 1
307
L’istruzione iterativa: esempi
L’istruzione k = k + 1 appena eseguita è l’ultima della sequenza
all’interno dell’istruzione while (come si vede dai rientri).
L’esecuzione dell’istruzione while procede perciò ripartendo dalla
valutazione dell’espressione condizionale, che risulta di nuovo vera.
Le due istruzioni al suo interno verranno quindi ripetute una
seconda volta.
k = 1 k 2
while k <= 3:
print("Buongiorno") Buongiorno
k = k + 1
308
L’istruzione iterativa: esempi
Si esegue prima la funzione print, che stampa nella shell per la
seconda volta la stringa Buongiorno:
k = 1 k 2
while k <= 3:
print("Buongiorno") Buongiorno
k = k + 1 Buongiorno
309
L’istruzione iterativa: esempi
Si esegue poi l’istruzione di assegnamento, che incrementa il valore
associato a k:
k = 1 k 3
while k <= 3:
print("Buongiorno") Buongiorno
k = k + 1 Buongiorno
310
L’istruzione iterativa: esempi
Si riparte quindi dalla valutazione dell’espressione condizionale
dell’istruzione while, che è ancora vera: le istruzioni al suo interno
verranno quindi ripetute per la terza volta.
k = 1 k 3
while k <= 3:
print("Buongiorno") Buongiorno
k = k + 1 Buongiorno
311
L’istruzione iterativa: esempi
Il messaggio Buongiorno viene stampato per la terza volta nella
shell...
k = 1 k 3
while k <= 3:
print("Buongiorno") Buongiorno
k = k + 1 Buongiorno
Buongiorno
312
L’istruzione iterativa: esempi
... e il valore associato a k viene incrementato per la terza volta,
diventando ora 4:
k = 1 k 4
while k <= 3:
print("Buongiorno") Buongiorno
k = k + 1 Buongiorno
Buongiorno
313
L’istruzione iterativa: esempi
Si valuta quindi l’espressione condizionale, che questa volta risulta
falsa: l’esecuzione dell’istruzione while a questo punto termina
(senza che vengano di nuovo eseguite le istruzioni al suo interno),
e non essendoci nessuna istruzione successiva (scritta cioè con lo
stesso rientro della parola-chiave while) termina anche
l’esecuzione del programma.
k = 1 k 4
while k <= 3:
print("Buongiorno") Buongiorno
k = k + 1 Buongiorno
Buongiorno
314
L’istruzione iterativa: esempi
315
L’istruzione iterativa: esempi
316
L’istruzione iterativa: esempi
317
L’istruzione iterativa: esempi
318
L’istruzione iterativa: esercizi
319
L’istruzione iterativa: esercizi
320
L’istruzione iterativa: esercizi
321
L’istruzione iterativa: esercizi
322
L’istruzione iterativa: esercizi
323
L’istruzione iterativa: esercizi
a = 1
while a < 4:
b = 1
while b < 3:
print(a, b)
b = b + 1
a = a + 1
324
Esercizi di riepilogo
Testi e soluzioni saranno resi disponibili nella pagina web del corso
subito dopo lo svolgimento di ciascuna esercitazione.
325
Esempi di programmi Python
326
Struttura dei programmi
327
Esempi di programmi Python
328
Esempi di programmi Python
Il programma mostrato di seguito calcola il valore più piccolo in
una sequenza di numeri di lunghezza qualsiasi, acquisita attraverso
la tastiera. Anche la dimensione della sequenza è un dato
d’ingresso, ed è il primo a essere acquisito. Il programma è
disponibile nel file 21_minimo.py
330
Esempi di programmi Python
331
Esempi di programmi Python
332
Esempi di programmi Python
Questo programma (disponibile nel file 24_fattoriale.py) calcola il
fattoriale di un dato numero naturale n, definito come segue:
n! = 1 × 2 × . . . × (n − 1) × n, se n > 0,
n! = 1, se n = 0
333
Esempi di programmi Python
334
Esempi di programmi Python
335
Esempi di programmi Python
336
Esempi di programmi Python
337
Esempi di programmi Python
338
Esempi di programmi Python
339
Esempi di programmi Python
340
Esempi di programmi Python
Il programma seguente (disponibile nel file 30_numeri_primi_1.py)
verifica se un dato numero naturale N sia primo o meno.
Contrariamente agli esempi precedenti, nei quali il risultato è un valore
numerico (o una sequenza di cifre), in questo caso il risultato può essere
visto come un valore logico (vero o falso), e può quindi essere
rappresentato con i simboli True e False.
Il procedimento codificato da questo programma consiste nell’analisi (per
mezzo di un’istruzione iterativa) di tutti i possibili divisori di N, cioè i
valori compresi tra 2 e ⌊N/2⌋ (estremi inclusi, dove ⌊x ⌋ indica la parte
intera di x ), tenendo traccia, per mezzo della variabile primo, del fatto
che sia già stato trovato o meno un divisore.
A tale variabile si assegna inizialmente il valore True (non è stato ancora
trovato nessun divisore, quindi il numero viene considerato primo).
Durante l’iterazione le si assegnerà il valore False se verrà trovato un
divisore, e in questo caso l’iterazione potrà terminare senza completare
l’analisi di tutti i possibili divisori. Il risultato viene mostrato per mezzo
di un messaggio che dipende dal valore associato alla variabile primo.
341
Esempi di programmi Python
342
Esempi di programmi Python
343
Esempi di programmi Python
344
Esempi di programmi Python
345
Esempi di programmi Python
346
Esercizi
347
L’istruzione break
348
L’istruzione break: esempio
349
L’istruzione break: esempio
350
L’istruzione break: esercizi
351
Funzioni
352
Le funzioni nei linguaggi di alto livello
353
Funzioni predefinite e definite dall’utente
354
Utilità delle funzioni
355
La libreria del linguaggio Python
356
Caratteristiche delle funzioni
357
Esecuzione di una funzione: chiamata
358
Principali funzioni Python predefinite: input
359
Principali funzioni Python predefinite: print
360
Principali funzioni Python predefinite
361
Principali funzioni Python predefinite
▶ int(numero)
restituisce la parte intera di un numero
▶ float(numero)
restituisce il valore di numero come numero frazionario
(floating point)
▶ int(stringa)
se stringa contiene la rappresentazione di un numero intero,
restituisce il numero corrispondente a tale valore
▶ float(stringa)
se stringa contiene la rappresentazione di un numero qualsiasi
(sia intero che frazionario), restituisce il suo valore espresso
come numero frazionario
362
Principali funzioni Python predefinite
363
Principali funzioni Python predefinite: esempi
364
Principali funzioni Python predefinite: esempi
365
Ancora sulla sintassi della chiamata di funzione
366
Chiamata di funzione: esempi
367
Chiamata di funzione: sintassi degli argomenti
368
Argomenti delle chiamate di funzione: esempi
369
Librerie di funzioni del linguaggio Python
370
Principali funzioni della libreria math
funzione descrizione
cos(x) coseno (x deve essere espresso in radianti)
sin(x) seno (come sopra)
tan(x) tangente (come sopra)
acos(x) arco-coseno (x deve essere nell’intervallo [−1, 1])
asin(x) arco-seno (come sopra)
atan(x) arco-tangente
radians(x) converte in radianti un angolo espresso in gradi
degrees(x) converte in gradi un angolo espresso in radianti
exp(x) ex
log(x) ln x (logaritmo naturale)
log(x ,b) logb x
log10(x) log10 x
pow(x ,y ) xy
√
sqrt(x) x
371
Principali funzioni della libreria random
funzione descrizione
random() genera un numero reale nell’intervallo [0, 1) da
una distribuzione di probabilità uniforme (cioè,
ogni valore di tale intervallo ha la stessa proba-
bilità di essere “estratto”)
uniform(a,b) come sopra, nell’intervallo [a, b) (gli argomenti
sono numeri qualsiasi)
randint(a,b) genera un numero intero nell’insieme {a, . . . , b}
da una distribuzione di probabilità uniforme (gli
argomenti devono essere numeri interi)
372
L’istruzione from-import
373
L’istruzione from-import: esempi
374
L’istruzione from-import: esempi
375
La libreria math: costanti matematiche notevoli
Oltre a varie funzioni, nella libreria math sono definite due variabili
alle quali è associato il valore (approssimato) delle costanti
matematiche π (3,14. . . ) ed e (la base dei logaritmi naturali:
2,71. . . ):
▶ pi
▶ e
Anche per usare queste variabili è necessaria l’istruzione
from-import, in una delle due versioni:
▶ from math import *
▶ from math import pi, e
377
Definizione di nuove funzioni
378
Definizione di nuove funzioni: l’istruzione def
Sintassi:
def nome-funzione(par1 , . . . , parn ):
corpo della funzione
▶ nome-funzione è un nome simbolico scelto dal programmatore,
con gli stessi vincoli a cui sono soggetti i nomi delle variabili
▶ par1 , . . . , parn sono nomi (scelti dal programmatore) di
variabili, dette parametri della funzione, alle quali l’interprete
assegnerà i valori degli argomenti che verranno indicati nella
chiamata della funzione
▶ corpo della funzione è una sequenza di una o più istruzioni
qualsiasi, ciascuna scritta in una riga distinta, con un rientro
di almeno un carattere, identico per tutte le istruzioni
La prima riga della definizione (contenente i nomi della funzione e
dei parametri) è detta intestazione della funzione.
379
Definizione di nuove funzioni: l’istruzione return
380
Definizione e chiamata di una funzione
L’istruzione def dovrà essere eseguita una sola volta, prima della
esecuzione di una qualsiasi chiamata della funzione. In caso
contrario il nome della funzione non sarà riconosciuto
dall’interprete, e la sua chiamata produrrà un messaggio d’errore.
381
Chiamata di una funzione: meccanismo di esecuzione
382
Definizione di funzioni: esempio
383
Definizione di funzioni: esempio
La definizione di una funzione deve essere scritta in una finestra
dell’editor, e deve essere salvata in un file:
384
Definizione di funzioni: esempio
Dopo il salvataggio del file, la funzione che esso contiene non è
ancora “nota” all’interprete: una sua chiamata scritta nella shell
produrrà un messaggio d’errore:
385
Definizione di funzioni: esempio
Prima di chiamare una funzione è necessario eseguire l’istruzione
def nel file che ne contiene la definizione:
386
Definizione di funzioni: esempio
Si ricordi che l’esecuzione dell’istruzione def non comporta
l’esecuzione delle istruzioni della funzione:
387
Definizione di funzioni: esempio
Da questo momento è possibile chiamare la funzione dalla shell.
388
Ancora sui parametri di una funzione
389
Definizione di funzioni: esempi
390
Definizione di funzioni: esempi
391
Definizione di funzioni: esempi
def serie_armonica(m):
serie = 1
k = 2
while k <= m:
serie = serie + 1/k
k = k + 1
return serie
392
Funzioni senza argomenti o senza risultato
393
Definizione di funzioni: esempio
394
Definizione di funzioni: esempio
395
Definizione di più funzioni in uno stesso file
396
Definizione di più funzioni in un file: esempio
397
Chiamate di funzione all’interno di altre funzioni
398
Chiamate di funzione all’interno di altre funzioni: esempio
399
Chiamate di funzione all’interno di altre funzioni: esempio
400
Chiamate di funzione all’interno di altre funzioni: esempio
def circ(raggio):
circonferenza = 2*pi*raggio
return circonferenza
401
Chiamate di funzione all’interno di altre funzioni: esempio
402
Chiamate di funzione all’interno di altre funzioni
403
Chiamate di funzione all’interno di altre funzioni: esempio
404
Chiamate di funzione all’interno di altre funzioni: esempio
def stampa_numeri_primi(n):
print("I numeri primi tra 1 e", n, "sono:")
k = 1
while k <= n:
if numero_primo(k) == True:
print(k)
k = k + 1
def numero_primo(numero):
if numero == 1:
return False
primo = True
divisore = 2
while divisore <= numero/2:
if numero % divisore == 0:
return False
divisore = divisore + 1
return True
405
Chiamate di funzione all’interno di altre funzioni: esempio
406
Chiamate di funzione all’interno di altre funzioni: esempio
407
Chiamate di funzione all’interno di altre funzioni: esempio
408
Programmi che contengono definizioni di funzioni
409
Esempio
def circ(raggio):
circonferenza = 2*pi*raggio
return circonferenza
410
Esempio
411
Funzioni: variabili locali
412
Variabili locali: esempio
In questo esempio si può osservare che il valore associato alla variabile n,
definita nella shell prima della chiamata della funzione successore, non
viene modificato durante l’esecuzione della funzione dall’istruzione n = x
+ 1 (notare che la funzione non restituisce nessun valore):
413
Variabili locali: esempio
Lo stesso accade se la chiamata della funzione si trova nelle istruzioni di
un programma scritto in un file, rispetto alle variabili definite nello stesso
programma, come mostra l’esempio seguente:
414
Variabili locali
415
Variabili locali: esempio
416
Variabili locali
417
Funzioni: variabili globali
418
Variabili globali: esempio
Nella funzione prova si fa riferimento al valore associato alla
variabile n senza che a essa sia stato assegnato in precedenza alcun
valore all’interno della stessa funzione: in questo caso l’interprete
accede alla variabile n già definita nella shell.
419
Variabili globali: esempio
420
Strutturazione dei programmi
421
Strutturazione dei programmi
422
Strutturazione dei programmi: esempio
423
Strutturazione dei programmi: esempio
424
Strutturazione dei programmi: esempio
425
Strutturazione dei programmi: esempio
def programma_principale():
n = input("Inserire un numero naturale: ")
stampa_numeri_primi(n)
426
Strutturazione dei programmi: esempio
427
Tipi dai dato strutturati
428
Tipi di dato nei linguaggi di alto livello
Abbiamo già incontrato alcuni tra i principali tipi di dato del lin-
guaggio Python: numeri interi, numeri frazionari, stringhe, valori
logici.
429
Tipi di dato Python: esempi
430
Tipi semplici e strutturati
431
Tipi di dato struttuati di Python
Oltre alle stringhe, due dei principali tipi strutturati del linguaggio
Python considerati in questo corso sono:
▶ le liste, che consentono di rappresentare sequenze ordinate
di valori qualsiasi
▶ i dizionari, che consentono di rappresentare collezioni (non
ordinate) di valori qualsiasi
432
Il tipo di dato lista
433
Il tipo di dato lista
434
Il tipo di dato lista
435
Esempi
436
Il tipo di dato lista
Oltre che valori espliciti, gli elementi di una lista possono essere
indicati come valori di espressioni.
437
Il tipo di dato lista
438
Esempi
439
Il tipo di dato lista
Come per gli altri tipi di dato, anche per le liste il linguaggio
Python mette a disposizione dei programmatori diversi operatori e
funzioni predefinite.
In particolare, essendo le liste un tipo strutturato, alcuni operatori
consentono l’accesso ai singoli elementi di una lista.
Il meccanismo di accesso si basa sul fatto che ogni elemento è
identificato univocamente dalla sua posizione (si ricordi che una
lista è una sequenza ordinata di valori).
La posizione di ciascun elemento di una lista è rappresentata in
linguaggio Python attraverso un numero intero, detto indice.
Per convenzione, l’indice del primo elemento di una lista è 0,
l’indice del secondo elemento è 1, e così via.
440
Principali operatori sulle liste
sintassi descrizione
lista1 == lista2 confronto (“uguale a”)
lista1 != lista2 confronto (“diverso da”)
espressione in lista verifica della presenza di un valore in una lista
espressione not in lista verifica dell’assenza di un valore in una lista
lista1 + lista2 concatenazione
lista[indice] indicizzazione: accesso a un elemento
lista[indice1 :indice2 ] slicing (sezionamento): accesso a una sotto-
sequenza di elementi
441
Operatori di confronto
442
Gli operatori in e not in
443
Esempi
444
L’operatore di concatenazione
445
Esempi
446
L’operatore di indicizzazione
447
L’operatore di indicizzazione
448
Esempi
449
L’operatore di slicing
L’operatore di slicing (“sezionamento”) restituisce una lista composta da
una sottosequenza di una data lista.
Sintassi:
▶ lista[indice1 :indice2 ]
▶ lista[indice1 :]
▶ lista[:indice2 ]
▶ lista[:]
dove indice1 e indice2 sono espressioni i cui valori devono essere numeri
interi compresi tra 0 e la lunghezza di lista.
Semantica: il risultato è una lista composta dagli elementi di lista aventi
indici da indice1 a indice2 − 1 (l’elemento di indice indice2 non viene
incluso nel risultato). Se indice1 è omesso, viene considerato pari a 0; se
indice2 viene omesso, viene considerato pari alla lunghezza della lista. Ne
consegue che lista[:] restituisce una lista identica a quella originale.
Anche in questo caso la lista originale non viene modificata.
450
Esempi
451
Funzioni predefinite che operano sulle liste
Le seguenti funzioni predefinite sono immediatamente disponibili nella
shell e nei propri programmi:
nome descrizione
len(lista) restituisce la lunghezza di una lista
min(lista) restituisce l’elemento più piccolo in una lista
composta da numeri
max(lista) restituisce l’elemento più grande in una lista
composta da numeri
range(a) restituisce un valore che può essere trasformato
nella lista [0, 1,. . . ,a-1] se a > 0 (il valore
associato ad a deve essere un intero) mediante
la funzione list
range(a,b) come sopra (i valori di a e b devono essere numeri
interi): se a < b consente di costruire la lista
[a,a+1,. . . ,b-1]
list(x) costruisce una lista corrispondente al valore x
restituito da range
452
Esempi
453
La funzione predefinita split
454
La funzione predefinita split
455
La funzione predefinita split
Se si desidera suddividere una stringa in corrispondenza di una
sequenza di uno o più caratteri qualsiasi, tale sequenza dovrà
essere indicata (sotto forma di una stringa) come argomento di
split, con la seguente sintassi:
stringa.split(caratteri)
456
La funzione split: esempi
457
La funzione split: esempi
458
La funzione predefinita strip
459
La funzione predefinita strip
460
La funzione strip: esempi
461
Esempio: costruzione di una lista
In molti programmi è necessario costruire una lista composta da valori
che dovranno essere acquisiti durante l’esecuzione degli stessi
programmi. La lista non può quindi essere scritta in modo esplicito al
loro interno, ma dovrà essere ottenuta come risultato di un’opportuna
sequenza di operazioni.
Per esempio, il seguente programma (disponibile nel file
43_costruzione_lista.py) costruisce una lista di cinque numeri
acquisiti tramite la tastiera, partendo da una lista vuota e usando
l’operatore di concatenazione:
lista = []
print("Inserire cinque numeri.")
k = 1
while k <= 5:
elemento = eval(input("Prossimo valore: "))
lista = lista + [elemento]
k = k + 1
print("La lista è:", lista)
462
Iterazione sugli elementi di una lista
463
Iterazione sugli elementi di una lista
k = 0
while k < len(lista):
istruzioni che coinvolgono lista[k]
k = k + 1
464
Esempio
465
Esempio
466
Esempio
467
Esempio
def somma_lista(lista):
somma = 0
k = 0
while k < len(lista):
somma = somma + lista[k]
k = k + 1
return somma
468
Esempio
def massimo_lista(lista):
massimo = lista[0]
k = 1
while k < len(lista):
if lista[k] > massimo:
massimo = lista[k]
k = k + 1
return massimo
469
Esempio
470
Esempio
def ordinati(lista):
i = 0
while i < len(lista) - 1:
if lista[i] > lista[i+1]:
return False
i = i + 1
return True
471
Esempio
472
Esempio
473
Stringhe e liste: sequenze
474
Stringhe e liste: sequenze
475
Esempi
476
Accesso agli elementi di sequenze nidificate
Si è detto che gli elementi di una lista possono essere valori di tipi
qualsiasi, quindi anche strutturati, come stringhe o altre liste.
L’operatore di indicizzazione consente di accedere anche agli
elementi di strutture nidificate.
Se s è una variabile a cui è stata assegnata una lista, e l’elemento
di indice i è a sua volta una sequenza (lista o stringa), sarà
possibile accedere all’elemento di indice j di quest’ultima con la
seguente sintassi: s[i][j]
477
Esempi
478
Liste nidificate: esempio
479
Liste nidificate: esempio
480
Liste nidificate: esempio
481
Liste nidificate: esempio
482
Liste nidificate: esempio
483
Liste nidificate: esempio
484
Liste nidificate: esempio
def stampa_matrice(M):
indice_riga = 0
while indice_riga < len(M):
indice_colonna = 0
while indice_colonna < len(M[indice_riga]):
print(M[indice_riga][indice_colonna])
indice_colonna = indice_colonna + 1
indice_riga = indice_riga + 1
485
Liste nidificate: esempio
486
Liste nidificate: esempio
Questa funzione (disponibile nel file 51-somma_matriciale.py)
restituisce la somma di due matrici (che si assumono avere le stesse
dimensioni) ricevute come argomento.
Come nel caso della somma vettoriale, la lista contenente il risultato
viene costruita per concatenazione.
487
Liste nidificate: esempio
488
L’istruzione iterativa for
489
L’istruzione iterativa for: sintassi
for v in s:
sequenza di istruzioni
▶ v deve essere il nome di una variabile che non sia già usata
per memorizzare dati all’interno dello stesso programma
▶ s deve essere un’espressione avente come valore una sequenza
(una lista o una stringa)
▶ sequenza di istruzioni è una sequenza di una o più istruzioni
qualsiasi che devono essere scritte rispettando la stessa regola
sui rientri già vista per l’istruzione while, e che di norma
eseguono un’operazione sulla variabile v
490
L’istruzione iterativa for: semantica
491
Esempio
492
Esempio
493
Esempio
494
Esempio
495
Esempio
def somma_lista(lista):
somma = 0
for numero in lista:
somma = somma + numero
return somma
496
Esempio
def massimo_lista(lista):
massimo = lista[0]
for numero in lista:
if numero > massimo:
massimo = numero
return massimo
497
Esempio
def stampa_matrice(M):
for riga in M:
for elemento in riga:
print(elemento)
498
Confronto tra while e for
499
Confronto tra while e for
500
Confronto tra while e for
501
Esempio
Tra le due iterazioni di questo programma, solo quella realizzata con
while consente di modificare i valori degli elementi di una lista:
502
Confronto tra while e for
503
Esempio
def ordinati(lista):
for i in range(len(lista) - 1):
if lista[i] > lista[i+1]:
return False
return True
504
Liste e istruzione di assegnamento
505
Liste e istruzione di assegnamento
506
Liste e istruzione di assegnamento
507
Liste e istruzione di assegnamento
508
Liste come argomenti di funzioni
509
Liste come argomenti di funzioni
510
Uso dell’operatore di slicing per la copia di una lista
Se il valore associato a una variabile (per es., x) fosse una lista, e
si volesse associare a un’altra variabile (per es., y) una copia della
stessa lista, si potrebbe usare l’operatore di slicing:
y = x[:]
Esempi:
▶ dopo le seguenti istruzioni:
x = [1,2,3]
y = x[:]
x[0] = 10
il valore associato a x è [10,2,3], quello associato a y è
ancora [1,2,3]
▶ analogamente, dopo le istruzioni:
w = [1,2,3]
azzera(w[:])
il valore associato a w è ancora [1,2,3]
511
Il tipo di dato dizionario
Un altro caso comune nella pratica è quello in cui i valori dei dati
da elaborare siano rappresentabili come collezioni (insiemi) di
valori più semplici e non ordinati, ciascuno dei quali abbia un
significato che possa essere descritto con un nome simbolico.
512
Il tipo di dato dizionario
513
Il tipo di dato dizionario
514
Il tipo di dato dizionario: esempi
515
Il tipo di dato dizionario
516
Esempi
517
Il tipo di dato dizionario
518
Il tipo di dato dizionario
519
Esempi
520
Il tipo di dato dizionario
521
Principali operatori sui dizionari
sintassi descrizione
dizionario1 == dizionario2 confronto (“uguale a”)
dizionario1 != dizionario2 confronto (“diverso da”)
dizionario[chiave] indicizzazione: accesso ai sin-
goli elementi
522
Operatori di confronto
523
Esempi
524
L’operatore di indicizzazione
525
L’operatore di indicizzazione
526
Esempi
527
Esempio: costruzione di un dizionario
528
Esempio: costruzione di un dizionario
persona = {}
print("Inserire i seguenti dati anagrafici:")
persona["nome"] = input("Nome: ")
persona["cognome"] = input("Cognome: ")
persona["età"] = eval(input("Età: "))
print("I dati inseriti sono:", persona)
529
Esempio: costruzione di un dizionario
530
Esempio: accesso agli elementi di un dizionario
def stampa_dizionario(persona):
print("I dati anagrafici sono i seguenti:")
print("Nome:", persona["nome"])
print("Cognome:", persona["cognome"])
print("Età:", persona["età"])
531
Esempio: accesso agli elementi di un dizionario
532
Esempio: somma vettoriale
533
Esempio: somma vettoriale
534
Accesso agli elementi di strutture nidificate in un dizionario
535
Esempi
536
Esempi
537
Esempi
persona = {}
print("Inserire i seguenti dati anagrafici:")
persona["nome"] = input("Nome: ")
persona["cognome"] = input("Cognome: ")
persona["luogo_nascita"] = input("Luogo di nascita: ")
persona["data_nascita"] = {}
print("Data di nascita:")
persona["data_nascita"]["giorno"] = eval(input("giorno: "))
persona["data_nascita"]["mese"] = eval(input("mese: "))
persona["data_nascita"]["anno"] = eval(input("anno: "))
print("I dati immessi sono i seguenti:")
print(persona)
538
Esempi
539
Esempi
persone = []
n_persone = eval(input("Quante persone? "))
n = 1
while n <= n_persone:
persona = {}
print("Dati anagrafici della persona n. " + str(n) + ":")
persona["nome"] = input(" nome: ")
persona["cognome"] = input(" cognome: ")
persona["luogo_nascita"] = input(" luogo di nascita: ")
persona["data_nascita"] = {}
print(" data di nascita:")
persona["data_nascita"]["giorno"] = eval(input(" giorno: "))
persona["data_nascita"]["mese"] = eval(input(" mese: "))
persona["data_nascita"]["anno"] = eval(input(" anno: "))
persone = persone + [persona]
n = n + 1
print("I dati immessi sono i seguenti:")
print(persone)
540
Esempi
541
Esempi
542
Esempi
In questo esempio si vuole memorizzare in un dizionario l’esito di una
gara di salto in alto di un singolo atleta: nome, cognome e numero di
gara dell’atleta, e misura (espressa in cm) di ogni salto valido; gli
eventuali salti non validi dovranno essere codificati con il valore 0.
Poiché il numero di prove in una gara di salto in alto non è definito a
priori ma dipende dall’esito di ciascuna prova (e può variare da atleta ad
atleta), è conveniente memorizzare in una lista la sequenza delle misure.
Il numero delle prove dovrà prima essere chiesto all’utente.
La lista con le misure di ciascuna prova può a sua volta essere
memorizzata all’interno di un dizionario che conterrà altri tre valori,
corrispondenti al nome, al cognome e al numero di gara dell’atleta.
Per esempio, nel caso di un atleta di nome Marco Bianchi, numero di
gara 24, che abbia eseguito sei prove delle quali tre valide (con misure
180, 185 e 188 cm) e tre nulle (la seconda, la terza e la sesta), il
dizionario da costruire sarà:
{"nome": "Marco", "cognome": "Bianchi", "numero": 24,
"prove": [180, 0, 0, 185, 188, 0]}
543
Esempi
544
Esempi
545
Esempi
546
Esempi
Il programma seguente è disponibile nel file 66_esame.py
studenti = []
n_studenti = eval(input("Quanti studenti? "))
n = 1
while n <= n_studenti:
studente = {}
print("Dati dello studente n. " + str(n) + ":"))
studente["nome"] = input(" nome: ")
studente["cognome"] = input(" cognome: ")
studente["matricola"] = input(" matricola: ")
studente["voto"] = eval(input(" voto: "))
studenti = studenti + [studente]
n = n + 1
print("I dati inseriti sono i seguenti:")
print(studenti)
547
Scrittura di programmi: definizione delle strutture dati
548
Scrittura di programmi: definizione delle strutture dati
549
Scrittura di programmi: definizione delle strutture dati
550
Definizione di strutture dati: esempi
551
Definizione di strutture dati: esempi
552
Definizione di strutture dati: esempi
553
Definizione di strutture dati: esempi
554
Definizione di strutture dati: esempi
555
Definizione di strutture dati: esempi
556
Definizione di strutture dati: esempi
Nel caso di spazi a più di tre dimensioni, le liste sono una scelta
più opportuna.
557
Definizione di strutture dati: esempi
558
Definizione di strutture dati: esempi
559
Definizione di strutture dati: esempi
560
Definizione di strutture dati: esempi
561
Definizione di strutture dati: esempi
In questo caso è preferibile usare una lista, anche se tra i valori che
si devono elaborare non esiste nessun ordinamento predefinito
(come nel caso di un insieme di studenti o di atleti).
L’uso di un dizionario richiederebbe infatti al programmatore la
scelta di una chiave distinta per ciascun valore da memorizzare al
suo interno, ma in casi come quelli degli esempi precedenti non
esistono scelte delle chiavi che rendano più semplice la
comprensione del significato o del ruolo degli elementi di un
dizionario rispetto agli indici di una lista.
Si ripensi per esempio al caso delle coordinate di un punto in uno
spazio a più di tre dimensioni, discusso in precedenza.
562
Definizione di strutture dati: esempi
Per capire meglio questo punto, si consideri ancora il caso dei dati
su un esame sostenuto da un insieme di studenti.
Se si volessero memorizzare i dati di ogni studente come elementi
di un dizionario (a loro volta contenuti in un dizionario con chiavi
come "nome", "matricola", ecc.), si dovrebbero usare chiavi
come "studente1", "studente2", ecc. Per esempio:
{"studente1": {"matricola":"12345", "nome": "Luca", ... },
"studente2": {"matricola":"54321", "nome": "Ugo", ... } }
Se l’intero dizionario fosse memorizzato in una variabile di nome
studenti, i dati di ogni studente sarebbero accessibili mediante
espressioni come studenti["studente2"].
563
Definizione di strutture dati: esempi
564
Dizionari come valori mutabili
565
Esempi
566
Copia di un dizionario
Si è visto che per eseguire una copia di una lista è possibile usare
l’operatore di slicing.
Per eseguire una copia di un dizionario è invece possibile usare la
funzione predefinita copy, che prevede la seguente sintassi:
dizionario.copy()
567
Esempi
568
Algoritmi di ricerca e ordinamento
569
Problemi di ricerca e di ordinamento
570
Algoritmo di ricerca sequenziale
571
Algoritmo di ricerca sequenziale
572
Algoritmo di ricerca sequenziale
573
Algoritmo di ricerca sequenziale
Due versioni della stessa funzione che fanno uso delle istruzioni while e
for (si veda il file 67_ricerca_sequenziale.py):
574
Algoritmo di ricerca binaria
Per il caso particolare in cui gli elementi della sequenza siano ordinati in
senso crescente o decrescente (secondo un dato criterio), esiste un
algoritmo più efficiente.
Tale algoritmo si basa su un procedimento analogo a quello che si
seguirebbe nella ricerca della pagina contenente un dato nome in un
elenco telefonico. Informalmente: si apre l’elenco in corrispondenza delle
due pagine centrali; se il nome cercato si trova in tali pagine la ricerca
(della pagina) termina; altrimenti:
▶ se il nome cercato precede (in ordine alfabetico) quelli presenti nelle
pagine in esame, la ricerca prosegue in modo analogo nelle pagine
precedenti (aprendo cioè l’elenco a metà di tali pagine, ecc.)
▶ se il nome cercato segue quelli nelle pagine considerate, la ricerca
procede in modo analogo nelle pagine successive
Da qui il nome di algoritmo di ricerca binaria.
575
Algoritmo di ricerca binaria
Per una descrizione più rigorosa si consideri una sequenza S di lunghezza
qualsiasi, composta da numeri disposti in ordine crescente, e un valore x
da cercare al suo interno.
Si inizia confrontando x con l’elemento in posizione centrale di S (si
indichi con c tale elemento); se x = c la ricerca termina e la risposta è
“vero”; in caso contrario:
▶ se x < c, la ricerca procede in modo analogo nella sottosequenza
di S che precede c
▶ se x > c, la ricerca procede in modo analogo nella sottosequenza
di S che segue c
È facile rendersi conto che se x non fa parte di S, dopo un numero finito
di confronti la sottosequenza da analizzare sarà vuota: in questo caso
l’algoritmo terminerebbe producendo il risultato “falso”.
Nota: detto N il numero di elementi della sequenza da analizzare in un
passo qualsiasi dell’algoritmo, se N è pari si può considerare come
elemento centrale quello in posizione N2 oppure N2 + 1.
576
Algoritmo di ricerca binaria: esempio
577
Algoritmo di ricerca binaria: esempio
578
Algoritmo di ricerca binaria: esempio
579
Algoritmo di ricerca binaria
A questo scopo si possono usare tre variabili per tener traccia degli
indici del primo elemento, dell’ultimo, e di quello in posizione
centrale della sottosequenza (lista) da analizzare in ogni iterazione.
Se l’elemento centrale non coincide con il valore cercato, in base
all’esito del confronto si modificherà il valore associato alla
variabile, corrispondente all’indice del primo oppure dell’ultimo
elemento, in modo che nella successiva iterazione tali indici
corrispondano agli estremi della nuova sottosequenza da
analizzare.
580
Algoritmo di ricerca binaria
581
Algoritmo di ricerca binaria: efficienza
582
Algoritmo di ricerca binaria: efficienza
Il numero k di confronti che vengono eseguiti nel caso peggiore per una
sequenza di N elementi ordinati non può essere calcolato esattamente,
contrariamente alla ricerca sequenziale. Può però essere approssimato
calcolando quanti confronti saranno necessari per ottenere una
sottosequenza di un solo elemento, assumendo che dopo ogni confronto
la lunghezza della sequenza da analizzare si riduca della metà, mediante il
ragionamento schematizzato di seguito:
583
Confronto tra ricerca sequenziale e binaria
È ora possibile confrontare l’efficienza dei due algoritmi di ricerca.
Il numero di confronti richiesto nel caso peggiore, per sequenze ordinate
di N elementi, è pari a:
▶ N, per la ricerca sequenziale
▶ ⌈log2 N + 1⌉ (circa), per la ricerca binaria
Poiché N ≥ ⌈log2 N + 1⌉ per qualsiasi intero N, si può concludere che nel
caso peggiore la ricerca binaria è più efficiente di quella sequenziale. Il
vantaggio è evidente al crescere di N, come mostra il seguente esempio:
584
Algoritmi di ordinamento
585
Algoritmi di ordinamento
586
Ordinamento per selezione
587
Ordinamento per selezione: esempio
Si vuole ordinare in senso crescente la sequenza:
589
Ordinamento per selezione
590
Ordinamento per selezione
def ordinamento_per_selezione(sequenza):
i = 0
while i < len(sequenza) - 1:
indice_minimo = i
j = i + 1
while j < len(sequenza):
if sequenza[j] < sequenza[indice_minimo]:
indice_minimo = j
j = j + 1
if indice_minimo != i:
temp = sequenza[i]
sequenza[i] = sequenza[indice_minimo]
sequenza[indice_minimo] = temp
i = i + 1
591
Ordinamento per inserimento
592
Ordinamento per inserimento: esempio
Si consideri la stessa sequenza dell’esempio precedente
(cont.)
▶ −48, −34, 52, 58, 75, −80, 22
gli elementi −48, . . . , 75 vengono spostati di una posizione
verso destra, −80 viene inserito nella prima posizione
▶ −80, −48, −34, 52, 58, 75, 22
gli elementi 52, 58, 75 vengono spostati di una posizione
verso destra, 22 viene inserito nella quarta posizione
▶ −80, −48, −34, 22, 52, 58, 75
Risultato: −80, −48, −34, 22, 52, 58, 75.
594
Ordinamento per inserimento
595
Algoritmo di ordinamento per inserimento
def ordinamento_per_inserimento(sequenza):
i = 1
while i < len(sequenza):
temp = sequenza[i]
j = i - 1
while j >= 0 and sequenza[j] > temp:
sequenza[j + 1] = sequenza[j]
j = j - 1
sequenza[j + 1] = temp
i = i + 1
596
Algoritmi di ordinamento: efficienza
Entrambi gli algoritmi consistono in una serie di confronti tra
coppie di valori, e in un certo numero di assegnamenti, scambi o
spostamenti degli elementi della sequenza. È inoltre facile
convincersi che il numero di scambi o spostamenti non può essere
superiore a quello dei confronti.
L’efficienza può perciò essere valutata, in funzione della lunghezza
N della sequenza, in termini del numero di confronti tra coppie di
suoi elementi.
Si può ora osservare che nell’ordinamento per selezione il numero
di confronti non dipende dai valori della sequenza originale. Nel
caso dell’ordinamento per inserimento il numero di confronti
dipende invece dalla sequenza originale, e ci si può facilmente
convincere che il caso peggiore (quello che richiede il maggior
numero di confronti) si verifica quando tale sequenza è ordinata nel
senso opposto a quello desiderato.
597
Algoritmi di ordinamento: efficienza
L’ordinamento per selezione di una sequenza di N elementi richiede
l’analisi di N − 1 posizioni, e per ciascuna di esse l’analisi di tutte quelle
successive (per trovare l’elemento più piccolo).
Il numero di confronti per ciascuna delle N − 1 posizioni considerate è
quindi il seguente:
▶ posizione 1: N − 1 confronti
▶ posizione 2: N − 2 confronti
▶ ...
▶ posizione N − 1: un confronto
Il numero totale di confronti è quindi:
N−1
X N(N − 1)
(N − 1) + (N − 2) + . . . + 1 = k =
2
k=1
599
Algoritmi di ricerca e ordinamento: esercizi
600
Lettura e scrittura di dati su file
601
Strumenti di input/output
602
Gestione dei file nel linguaggio Python
603
Gestione dei file nel linguaggio Python
604
Procedimento per l’accesso ai file
605
Procedimento per l’accesso ai file
606
Apertura di un file: la funzione open
Sintassi:
variabile = open(nome-file, modalità)
▶ variabile: il nome della variabile che verrà associata al file
▶ nome-file: una stringa contenente il nome del file
▶ modalità: una stringa che indica la modalità di apertura
(lettura o scrittura)
607
Apertura di un file: la funzione open
Il nome del file che si desidera aprire deve essere passato come
argomento della funzione open sotto forma di stringa.
Il nome del file può essere:
▶ assoluto, cioè preceduto dalla sequenza (pathname) dei
nomi delle directory che lo contengono, a partire dalla
directory radice del file system, secondo la sintassi prevista
dal sistema operativo del proprio calcolatore
▶ relativo, cioè composto dal solo nome del file: questo è
possibile solo se la funzione open è chiamata da un
programma o da una funzione che si trovi nella stessa
directory che contiene il file da aprire
608
Apertura di un file: la funzione open
Per esempio, nel caso di un file di nome dati.txt che si trovi nella
directory C:\Users\Dati\ di un sistema operativo Windows:
▶ il nome relativo è dati.txt
▶ il nome assoluto è C:\Users\Dati\dati.txt
Se un file con lo stesso nome (dati.txt) è memorizzato nella
directory /Users/Dati/ di un sistema operativo Linux oppure
Mac OS:
▶ il nome relativo è ancora dati.txt
▶ il nome assoluto è /Users/Dati/dati.txt
609
Modalità di accesso ai file
610
Modalità di accesso ai file
611
Apertura di un file in lettura: esempi
612
Apertura di un file in lettura: esempi
613
Apertura di un file in lettura: esempi
Si ricordi che:
▶ tentare di aprire in lettura un file inesistente produce un
errore
▶ se la chiamata di open è scritta nella shell bisogna indicare il
nome assoluto del file (in caso contrario l’interprete cercherà
il file in una delle directory di installazione dell’ambiente
Python)
614
Apertura di un file in lettura: esempi
Nella directory C:\Users\user\Dati\ è presente solo il file dati.txt.
Le istruzioni scritte nella shell aprono in lettura tale file (indicandone il
nome assoluto), e tentano di aprire un file inesistente (risultati.txt).
615
Apertura di un file in lettura: esempi
L’istruzione scritta nella shell tenta di aprire il file dati.txt nella
directory C:\Users\user\Dati\ indicandone erroneamente il nome
relativo.
616
Apertura di un file in lettura: esempi
617
Apertura di un file in lettura: esempi
618
Apertura di un file in lettura: esempi
619
Apertura di un file in scrittura: esempi
620
Apertura di un file in scrittura: esempi
621
Apertura di un file in scrittura: esempi
622
Apertura di un file in scrittura: esempi
623
Chiusura di un file: la funzione close
Sintassi: variabile.close()
dove variabile deve essere la variabile usata nell’apertura dello stesso file
attraverso la funzione open (si noti la sintassi particolare della chiamata
di open, analoga a quella della funzione split).
Per esempio, assumendo che un file di nome dati.txt sia stato aperto
in lettura con l’istruzione
f = open("dati.txt", "r")
esso dovrà essere chiuso con la chiamata:
f.close()
624
Apertura contemporanea di più file
625
Scrittura in un file: la funzione write
In un file di testo che sia stato aperto in scrittura (in modalità "w"
oppure "a") è possibile scrivere dati sotto forma di stringhe (cioè
sequenze di caratteri) attraverso la funzione predefinita write.
Sintassi: variabile.write(stringa)
▶ variabile è la variabile associata al file
▶ stringa è una stringa contenente la sequenza di caratteri da
scrivere nel file
626
Scrittura in un file: la funzione write
627
Scrittura in un file: esempio
Prima riga.
Seconda riga.
628
Scrittura in un file: esempio
629
Scrittura in un file: esempio
630
Scrittura in un file: esempio
Prima riga.
Seconda riga.
Terza riga.
631
Scrittura in un file: esempio
632
Scrittura in un file: esempio
Nuova riga.
633
Scrittura in un file: esempio
634
Lettura da un file
635
Lettura da un file: la funzione read
636
La funzione read: esempio
Prima riga.
Seconda riga.
Terza riga.
f = open("testo.txt", "r")
s = f.read()
f.close()
637
La funzione read: esempio
638
Lettura da un file: la funzione read
Questo significa che dopo aver chiuso e riaperto (in lettura) uno
stesso file sarà possibile riacquisirne il contenuto con una chiamata
di read.
639
La funzione read: esempio
f = open("testo.txt", "r")
s1 = f.read()
f.close()
f = open("testo.txt", "r")
s2 = f.read()
f.close()
640
La funzione read: esempio
f = open("testo.txt", "r")
s1 = f.read()
s2 = f.read()
s3 = f.read()
f.close()
641
La funzione read: esempio
f = open("testo.txt", "r")
s = f.read()
f.close()
print("Il contenuto del file è:")
print(s)
642
La funzione read: esempio
643
Lettura da un file: la funzione readline
La sintassi è la seguente:
variabile.readline()
dove variabile indica come al solito la variabile associata al file.
644
Lettura da un file: la funzione readline
645
La funzione readline: esempio
Si consideri lo stesso file testo.txt degli esempi precedenti
(contenente tre righe), e si esegua il seguente programma dopo
averlo memorizzato nella stessa directory di tale file:
f = open("testo.txt", "r")
a = f.readline()
b = f.readline()
c = f.readline()
d = f.readline()
f.close()
Come si potrà vedere valutando le espressioni a, b, c e d nella shell,
le variabili corrispondenti conterranno rispettivamente le stringhe:
"Prima riga.\n"
"Seconda riga.\n"
"Terza riga."
"" (una stringa vuota)
646
Lettura da un file: la funzione readline
647
La funzione readline: esempio
f = open("testo.txt", "r")
print("Il file contiene le seguenti righe:")
riga = f.readline()
while riga != "".
print(riga)
riga = f.readline()
f.close()
648
La funzione readline: esempio
649
La funzione readline: esempio
Terza riga.
650
La funzione readline: esempio
651
Lettura da un file: la funzione readline
652
Lettura da un file: la funzione readlines
La sintassi è la seguente:
variabile.readlines()
dove variabile è ovviamente la variabile associata al file.
653
La funzione readlines: esempio
654
La funzione readlines: esempio
655
Lettura da un file: la funzione readlines
656
La funzione readlines: esempio
657
La funzione readlines: esempio
f = open("testo.txt", "r")
print("Il contenuto del file è:")
s = f.readlines()
f.close()
k = 0
while k < len(s):
print(s[k])
k = k + 1
658
La funzione readlines: esempio
659
La funzione readlines: esempio
f = open("testo.txt", "r")
print("Il contenuto del file è:")
righe = f.readlines()
f.close()
for riga in righe:
print(riga)
660
Lettura da un file: read, readline, readlines
661
Lettura da un file: read, readline, readlines
662
Lettura/scrittura su file: esercizi
663
Lettura/scrittura su file: esercizi
Una possibile soluzione, disponibile nel file 72_scrivi_testo.py:
print("Inserire una o più righe di testo,")
print(" e una riga vuota per concludere:")
f = open("testo.txt", "w")
riga = input()
while riga != "":
f.write(riga + "\n")
riga = input()
f.close()
Si noti l’aggiunta del carattere newline alla stringa contenente una riga
acquisita dalla tastiera con input. Questo è reso necessario dal fatto che
input non include il newline nella stringa che restituisce. In assenza di
tale carattere, l’intero testo inserito dall’utente verrebbe scritto nel file
senza interruzioni di riga (si provi a sostituire la chiamata
f.write(riga + "\n") con f.write(riga) e si osservi l’effetto).
Per verificare il corretto funzionamento del programma, dopo averlo
eseguito si apra il file testo.txt con un editor come Blocco note.
664
Lettura/scrittura su file: esercizi
665
Lettura/scrittura su file: esercizi
Si vuole scrivere un programma che acquisisca attraverso la tastiera una
matrice di dimensione qualsiasi, e la memorizzi in un nuovo file di nome
matrice.txt (sovrascrivendolo se già esistente).
La matrice dovrà essere scritta dall’utente sotto forma di una lista
composta a sua volta da liste, ciascuna delle quali corrisponda a una riga
della matrice. Per esempio, la matrice:
3 −1
0 4
3 -1
0 4
666
Lettura/scrittura su file: esercizi
In questa soluzione, disponibile nel file 74_scrivi_matrice.py, si
scorrono gli elementi della matrice con due istruzioni iterative nidificate, e
si scrive nel file ciascuno di essi (seguito da un carattere di spaziatura)
con una chiamata distinta di write.
Al termine di una riga della matrice (cioè al termine dell’iterazione
nidificata) si scrive nel file il carattere newline.
Si noti l’uso di eval per l’acquisizione della lista (la funzione input
restituirebbe infatti una stringa), e la conversione di ciascun elemento
(numero) della matrice in una stringa per mezzo della funzione str,
prima di poterlo scrivere nel file con write.
667
Lettura/scrittura su file: esercizi
Questo è
un esempio.
668
Lettura/scrittura su file: esercizi
669
Lettura/scrittura su file: esercizi
f = open("testo.txt", "r")
testo = f.read()
f.close()
print("Il file contiene le seguenti parole:")
parole = testo.split()
k = 0
while k < len(parole):
print(parole[k])
k = k + 1
670
Lettura/scrittura su file: esercizi
f = open("testo.txt", "r")
testo = f.read()
f.close()
print("Il file contiene le seguenti parole:")
parole = testo.split()
for parola in parole:
print(parola)
671
Lettura/scrittura su file: esercizi
672
Lettura/scrittura su file: esercizi
f = open("testo.txt", "r")
testo = f.readlines()
f.close()
print("Il file contiene le seguenti parole:")
for riga in testo:
parole = riga.split()
for parola in parole:
print(parola)
673
Lettura/scrittura su file: esercizi
674
Lettura/scrittura su file: esercizi
675
Lettura/scrittura su file: esercizi
f = open("matrice.txt", "r")
righe = f.readlines()
f.close()
m = []
for riga in righe:
elementi = riga.split()
riga_m = []
for valore in elementi:
riga_m = riga_m + [float(valore)]
m = m + [riga_m]
print("La matrice è:\n", m)
676
Lettura/scrittura su file: esercizi
677
Lettura/scrittura su file: esercizi
678
Lettura/scrittura su file: esercizi
Una soluzione elementare, disponbile nel file 77_conta_vocali.py:
def conta_vocali(nome_file):
f = open(nome_file, "r")
testo = f.read()
f.close()
vocali = {"a": 0, "e": 0, "i": 0, "o": 0, "u": 0}
for lettera in testo:
if lettera == "a":
vocali["a"] = vocali["a"] + 1
if lettera == "e":
vocali["e"] = vocali["e"] + 1
if lettera == "i":
vocali["i"] = vocali["i"] + 1
if lettera == "o":
vocali["o"] = vocali["o"] + 1
if lettera == "u":
vocali["u"] = vocali["u"] + 1
return vocali
679
Lettura/scrittura su file: esercizi
680
Lettura/scrittura su file: esercizi
Una soluzione più elegante si può ottenere tenendo conto che ciascuna
delle stringhe che costituiscono le chiavi del dizionario coincide con la
vocale corrispondente (questo programma è disponibile nel file
78_conta_vocali_2.py):
def conta_vocali(nome_file):
f = open(nome_file, "r")
testo = f.read()
f.close()
vocali = {"a": 0, "e": 0, "i": 0, "o": 0, "u": 0}
for lettera in testo:
if lettera == "a" or lettera == "e" or \
lettera == "i" or lettera == "o" or \
lettera == "u":
vocali[lettera] = vocali[lettera] + 1
return vocali
681
Lettura/scrittura su file: esercizi
def conta_vocali(nome_file):
f = open(nome_file, "r")
testo = f.read()
f.close()
vocali = {"a": 0, "e": 0, "i": 0, "o": 0, "u": 0}
for lettera in testo:
if lettera in "aeiou":
vocali[lettera] = vocali[lettera] + 1
return vocali
682
Lettura/scrittura su file: esercizi
683
Lettura/scrittura su file: esercizi
684
Lettura/scrittura su file: esercizi
Una possibile definizione della funzione è la seguente (si veda il file
79_esiti_esame.py). Per verificarne il funzionamento si può usare il
file allegato esiti_esame.txt
def esiti_esame(nome_file):
f = open(nome_file, "r")
esiti = f.readlines()
f.close()
studenti = []
for esito in esiti:
riga = esito.split()
studente = {}
studente["matricola"] = riga[0]
studente["nome"] = riga[1]
studente["cognome"] = riga[2]
studente["voto"] = int(riga[3])
studenti = studenti + [studente]
return studenti
685