![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Definiamo ora una classe chiamata Tempo che permette di registrare un'ora del giorno:
class Tempo:
pass
Possiamo creare un nuovo oggetto Tempo assegnando gli attributi per le ore, i minuti e i secondi:
Time = Tempo()
Time.Ore = 11
Time.Minuti = 59
Time.Secondi = 30
Il diagramma di stato per l'oggetto Time �:

Esercizio: scrivi una funzione StampaTempo che prende un oggetto Tempo come argomento e ne stampa il risultato nella classica forma ore:minuti:secondi.
Esercizio: scrivi una funzione booleana Dopo che prende come argomenti due oggetti Tempo (Tempo1 e Tempo2) e ritorna vero se Tempo1 segue cronologicamente Tempo2 e falso in caso contrario.
Nelle prossime funzioni scriveremo due versioni di una funzione che chiameremo SommaTempi che calcola la somma di due oggetti Tempo. Questo permetter� di mostrare due tipi di funzioni: le funzioni pure e i modificatori.
Questa � una versione grezza di SommaTempi:
def SommaTempi(T1, T2):
Somma = Tempo()
Somma.Ore = T1.Ore + T2.Ore
Somma.Minuti = T1.Minuti + T2.Minuti
Somma.Secondi = T1.Secondi + T2.Secondi
return Somma
La funzione crea un nuovo oggetto Tempo, inizializza i suoi attributi e ritorna un riferimento al nuovo oggetto. Questa viene chiamata funzione pura perch� non modifica in alcun modo gli oggetti passati come suoi parametri e non ha effetti collaterali (del tipo richiedere l'immissione di un valore da parte dell'operatore o stampare un valore a video).
Ecco un esempio che mostra come usare questa funzione: creiamo due oggetti Tempo, OraCorrente che contiene l'ora corrente e TempoCottura che indica il tempo necessario a preparare un pasto. Se non hai ancora scritto StampaTempo guarda la sezione 14.2 prima di continuare:
>>> OraCorrente = Tempo()
>>> OraCorrente.Ore = 9
>>> OraCorrente.Minuti = 14
>>> OraCorrente.Secondi= 30
>>> TempoCottura = Tempo()
>>> TempoCottura.Ore = 3
>>> TempoCottura.Minuti = 35
>>> TempoCottura.Secondi = 0
>>> PastoPronto = SommaTempi(OraCorrente, TempoCottura)
>>> StampaTempo(PastoPronto)
La stampa di questo programma � 12:49:30 ed il risultato � corretto. D'altra parte ci sono dei casi in cui il risultato � sbagliato: riesci a pensarne uno?
Il problema � che nella nostra funzione non abbiamo tenuto conto del fatto che le somme dei secondi e dei minuti possono andare oltre il 59. Quando capita questo dobbiamo conteggiare il riporto come facciamo con le normali addizioni.
Ecco una seconda versione corretta della funzione:
def SommaTempi(T1, T2):
Somma = Tempo()
Somma.Ore = T1.Ore + T2.Ore
Somma.Minuti = T1.Minuti + T2.Minuti
Somma.Secondi = T1.Secondi + T2.Secondi
if Somma.Secondi >= 60:
Somma.Secondi = Somma.Secondi - 60
Somma.Minuti = Somma.Minuti + 1
if Somma.Minuti >= 60:
Somma.Minuti = Somma.Minuti - 60
Somma.Ore = Somma.Ore + 1
return Somma
Questa funzione � corretta e comincia ad avere una certa lunghezza. In seguito ti mostreremo un approccio alternativo che ti permetter� di ottenere un codice pi� corto.
Ci sono dei casi in cui � utile una funzione che possa modificare gli oggetti passati come suoi parametri. Quando questo si verifica la funzione � detta modificatore.
La funzione Incremento che somma un certo numero di secondi a Tempo pu� essere scritta in modo molto intuitivo come modificatore. La prima stesura potrebbe essere questa:
def Incremento(Tempo, Secondi):
Tempo.Secondi = Tempo.Secondi + Secondi
if Tempo.Secondi >= 60:
Tempo.Secondi = Tempo.Secondi - 60
Tempo.Minuti = Tempo.Minuti + 1
if Tempo.Minuti >= 60:
Tempo.Minuti = Tempo.Minuti - 60
Tempo.Ore = Tempo.Ore + 1
La prima riga calcola il valore mentre le successive controllano che il risultato sia nella gamma di valori accettabili come abbiamo gi� visto.
Questa funzione � corretta? Cosa succede se il parametro Secondi � molto pi� grande di 60? In questo caso non � abbastanza il riporto 1 tra secondi e minuti, e quindi dobbiamo riscrivere il controllo per fare in modo di continuarlo finch� Secondi non diventa minore di 60. Una possibile soluzione � quella di sostituire l'istruzione if con un ciclo while:
def Incremento(Tempo, Secondi):
Tempo.Secondi = Tempo.Secondi + Secondi
while Tempo.Secondi >= 60:
Tempo.Secondi = Tempo.Secondi - 60
Tempo.Minuti = Tempo.Minuti + 1
while Tempo.Minuti >= 60:
Tempo.Minuti = Tempo.Minuti - 60
Tempo.Ore = Tempo.Ore + 1
La funzione � corretta, ma certamente non si tratta ancora della soluzione pi� efficiente possibile.
Esercizio: riscrivi questa funzione facendo in modo di non usare alcun tipo di ciclo.
Esercizio: riscrivi Incremento come funzione pura e scrivi delle chiamate di funzione per entrambe le versioni.
Qualsiasi cosa che pu� essere fatta con i modificatori pu� anche essere fatta con le funzioni pure e alcuni linguaggi di programmazione non prevedono addirittura i modificatori. Si pu� affermare che le funzioni pure sono pi� veloci da sviluppare e portano ad un minor numero di errori, anche se in qualche caso pu� essere utile fare affidamento sui modificatori.
In generale raccomandiamo di usare funzioni pure quando possibile e usare i modificatori come ultima risorsa solo se c'� un evidente vantaggio nel farlo. Questo tipo di approccio pu� essere definito stile di programmazione funzionale.
In questo capitolo abbiamo mostrato un approccio allo sviluppo del programma che possiamo chiamare sviluppo prototipale: siamo partiti con lo stendere una versione grezza (prototipo) che poteva effettuare solo i calcoli di base, migliorandola e correggendo gli errori man mano che questi venivano trovati.
Sebbene questo approccio possa essere abbastanza efficace pu� portare a scrivere un codice inutilmente complesso (perch� ha a che fare con molti casi speciali) e inaffidabile (dato che � difficile sapere se tutti gli errori sono stati rimossi).
Un'alternativa � lo sviluppo pianificato nel quale lo studio preventivo del problema da affrontare rende la programmazione molto pi� semplice. Nel nostro caso potremmo accorgerci che a tutti gli effetti l'oggetto Tempo � rappresentabile da tre cifre in base numerica 60.
Quando abbiamo scritto SommaTempi e Incremento stavamo effettivamente calcolando una somma in base 60 e questo � il motivo per cui dovevamo gestire il riporto tra secondi e minuti, e tra minuti e ore quando la somma era maggiore di 59.
Questa osservazione ci suggerisce un altro tipo di approccio al problema: possiamo convertire l'oggetto Tempo in un numero singolo ed avvantaggiarci del fatto che il computer lavora bene con le operazioni aritmetiche. Questa funzione converte un oggetto Tempo in un intero:
def ConverteInSecondi(Orario):
Minuti = Orario.Ore * 60 + Orario.Minuti
Secondi = Minuti * 60 + Orario.Secondi
return Secondi
Tutto quello che ci serve � ora un modo per convertire da un intero ad un oggetto Tempo:
def ConverteInTempo(Secondi):
Orario = Tempo()
Orario.Ore = Secondi/3600
Secondi = Secondi - Orario.Ore * 3600
Orario.Minuti = Secondi/60
Secondi = Secondi - Orario.Minuti * 60
Orario.Secondi = Secondi
return Orario
Forse dovrai pensarci un po' su per convincerti che questa tecnica per convertire un numero da una base all'altra � formalmente corretta. Comunque ora puoi usare queste funzioni per riscrivere SommaTempi:
def SommaTempi(T1, T2):
Secondi = ConverteInSecondi(T1) + ConverteInSecondi(T2)
return ConverteInTempo(Secondi)
Questa versione � molto pi� concisa dell'originale e ed � molto pi� facile dimostrare la sua correttezza.
Esercizio: riscrivi Incremento usando lo stesso principio.
Sicuramente la conversione numerica da base 10 a base 60 e viceversa � meno intuitiva da capire, data la sua astrazione. Il nostro intuito ci aveva portato a lavorare con i tempi in un modo molto pi� comprensibile anche se meno efficace.
Malgrado lo sforzo iniziale abbiamo progettato il nostro programma facendo in modo di trattare i tempi come numeri in base 60, il tempo investito nello scrivere le funzioni di conversione viene abbondantemente recuperato quando riusciamo a scrivere un programma molto pi� corto, facile da leggere e correggere, e soprattutto pi� affidabile.
Se il programma � progettato in modo oculato � anche pi� facile aggiungere nuove caratteristiche. Per esempio immagina di sottrarre due tempi per determinare l'intervallo trascorso. L'approccio iniziale avrebbe portato alla necessit� di dover implementare una sottrazione con il prestito. Con le funzioni di conversione, scritte una sola volta ma riutilizzate in varie funzioni, � molto pi� facile e rapido avere un programma funzionante anche in questo caso.
Talvolta il fatto di rendere un problema pi� generale e quindi leggermente pi� difficile da implementare permette di gestirlo in modo pi� semplice dato che ci sono meno casi speciali da gestire e di conseguenza minori possibilit� di errore.
Quando trovi una soluzione ad una classe di problemi, invece che ad un singolo problema, hai a che fare con un algoritmo. Abbiamo gi� usato questa parola in precedenza ma non l'abbiamo propriamente definita ed il motivo risiede nel fatto che non � facile trovare una definizione. Proveremo un paio di approcci.
Consideriamo qualcosa che non � un algoritmo: quando hai imparato a moltiplicare due numeri di una cifra hai sicuramente memorizzato la tabella della moltiplicazione. In effetti si � trattato di memorizzare 100 soluzioni specifiche: questo tipo di lavoro non � un algoritmo.
Ma se sei stato "pigro" probabilmente hai trovato delle scorciatoie che ti hanno permesso di alleggerire il lavoro. Per fare un esempio per moltiplicare n per 9 potevi scrivere n-1 come prima cifra, seguito da 10-n come seconda cifra. Questo sistema � una soluzione generale per moltiplicare ogni numero di una cifra maggiore di zero per 9: in questo caso ci troviamo a che fare con un algoritmo.
Le varie tecniche che hai imparato per calcolare la somma col riporto, la sottrazione con il prestito, la moltiplicazione, la divisione sono tutti algoritmi. Una delle caratteristiche degli algoritmi � che non richiedono intelligenza per essere eseguiti in quanto sono processi meccanici nei quali ogni passo segue il precedente secondo un insieme di regole pi� o meno semplice.
D'altro canto la progettazione di algoritmi � molto interessante ed intellettualmente stimolante rappresentando il fulcro di ci� che chiamiamo programmazione.
Alcune delle cose pi� semplici che facciamo naturalmente, senza difficolt� o pensiero cosciente, sono tra le cose pi� difficili da esprimere sotto forma di algoritmo. Comprendere un linguaggio naturale � un buon esempio: lo sappiamo fare tutti ma finora nessuno � stato in grado di spiegare come ci riusciamo esprimendolo sotto forma di algoritmo.
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |