Il primo programma: Euclide in Python#
Python ha una qualità che conta più della sua fama: si legge quasi come una lista di istruzioni scritta in inglese, e per chi non ha mai programmato è la differenza fra seguire un procedimento e decifrarlo. Su questa base è cresciuto un ecosistema di librerie per il calcolo e per il machine learning, e tre raccolte di codice già pronto torneranno di continuo: NumPy, che fa i conti su lunghe sequenze di numeri tutte insieme e in fretta; scikit-learn, dove stanno i metodi classici del machine learning; e PyTorch, con cui si costruiscono e si addestrano le reti neurali.
Cominciamo dal massimo comune divisore, perché il procedimento lo conosciamo già per intero e resta solo da dirlo in una lingua che la macchina esegue.
Calcolare il massimo comune divisore#
Il trucco di Euclide lo abbiamo già visto: dividere, tenere il resto e ripetere, finché il resto non diventa zero. Qui lo traduciamo in Python e, per metterlo alla prova, lo applichiamo a una coppia di numeri nuova, \(60\) e \(48\).
# La ricetta: riceve due numeri interi positivi e ne restituisce il MCD
def mcd(a, b):
while b:
a, b = b, a % b
return a
# Proviamola
print(mcd(60, 48))
12
In un programma il segno = non
vuol dire «è uguale a», come in matematica. Vuol dire «da adesso questo nome
indica questo valore». a = 5 non è un’affermazione da verificare, è un ordine:
da adesso in poi, dove c’è scritto a c’è il numero cinque. Si chiama
assegnazione, e le assegnazioni sono la cosa che un programma fa più spesso
di ogni altra.
Ora leggiamolo riga per riga, come una ricetta:
def mcd(a, b):dà un nome alla ricetta e ai suoi due ingredienti, i numeriaeb. In programmazione una ricetta con un nome si chiama funzione; è un parente lontano della funzione dei grafici (anche lei riceve dei numeri e ne restituisce uno), ma qui è soprattutto un modo per scrivere una volta un procedimento e poi riusarlo quando serve;while b:significa «ripeti finchébnon è diventato zero». Lo zero non è scritto perché Python, quando gli si dà un numero al posto di una condizione, considera vero qualunque numero diverso da zero:while bsi legge quindi «finchébc’è ancora»;a % bè il simbolo Python per il resto della divisione traaeb: proprio il resto del trucco di Euclide;a, b = b, a % bfa scorrere i numeri di un posto: il divisore prende il posto del numero grande, e il resto prende il posto del divisore. Qui c’è un dettaglio che conta davvero: Python calcola tutti e due i valori a destra dell’uguale usando i numeri vecchi, e solo dopo li scrive a sinistra. Le due assegnazioni avvengono insieme, non una dopo l’altra;return aconsegna il risultato a chi ha chiesto il calcolo: quando il resto è zero, l’ultimo numero per cui si è diviso è il MCD;print(mcd(60, 48))è la riga che mette in moto la ricetta con i due numeri e ne stampa a schermo il risultato. È l’unica che produce qualcosa da vedere.
Seguiamolo con \(60\) e \(48\): \(60\) diviso \(48\) dà resto \(12\); poi \(48\) diviso \(12\)
dà resto \(0\). Resto zero: l’ultimo numero per cui abbiamo diviso è \(12\), ed è
proprio quello che print ha stampato sopra.
Se quelle due assegnazioni fossero davvero una dopo l’altra il risultato cambierebbe, ed è l’errore in cui si cade seguendo il codice con carta e penna. Scritte così:
a = b # a diventa 48
b = a % b # ma qui «a» vale già 48, non più 60!
la seconda riga calcolerebbe \(48\) diviso \(48\), cioè resto \(0\), il ciclo si fermerebbe subito e la funzione restituirebbe \(48\) invece di \(12\). Un numero che non è nemmeno un divisore comune di \(60\) e \(48\): sbagliato di netto, non di poco.
Il commento sopra la funzione dice «due numeri interi positivi», e non è una
formalità: con i numeri negativi il risultato può uscire negativo (mcd(12, -8)
restituisce \(-4\)), il che non è sbagliato di suo, ma un massimo comune divisore
lo si vuole positivo. Le funzioni già pronte delle librerie se ne occupano (in
Python è math.gcd, che non restituisce mai un numero negativo); questa, che è
la versione minima, no. Ed è una cosa che tornerà spesso: quattro righe che
funzionano non sono ancora quattro righe robuste.
Questo non è ancora intelligenza artificiale: è un algoritmo, e per giunta uno dei più antichi che esistano. Il massimo comune divisore di \(60\) e \(48\) lo si trova anche a mente; il pregio dell’idea di Euclide è che continua a funzionare quando i numeri diventano lunghi cento cifre e a mente non ci si prova nemmeno, ed è per questo che dopo duemilatrecento anni sta ancora dentro le librerie di quasi ogni linguaggio.
La funzione mcd è una ricetta scritta da una persona, riga per riga, ed è il
termine di paragone di tutto il resto: i programmi di cui parleremo quella
ricetta non ce l’hanno scritta dentro, e se la costruiscono guardando migliaia
di esempi. Per scriverne uno servono ancora un po’ di
Python e un po’ di
matematica. Prima, però, c’è una domanda che
Euclide lascia aperta, e la tratta la sezione Che cosa si può
calcolare: che cosa può fare, in generale, una ricetta del
genere, e che cosa nessuna ricetta potrà mai fare.