Subsections

6. Debugging

6.1 Cos'è il Debugging?

Poco dopo aver iniziato a programmare ci siamo accorti di come non fosse affatto facile far fare ai programmi quello che volevamo. Avevamo scoperto il Debugging. Posso ricordare il preciso momento in cui ho realizzato che avrei passato buona parte della mia vita a scoprire e correggere gli errori nei miei stessi programmi.

- Maurice Wilkes scopre il debugging, 1949

Se finora avete ripetuto tutti i programmi degli esempi vi sarete certamente accorti che a volte (spesso) il programma da voi scritto assume comportamenti differenti da quelli che avevate previsto. È molto comune. Il Debugging è il processo grazie al quale portate il programma a svolgere le funzioni per cui è stato scritto correggendo gli errori, e vi assicuro, può essere un'operazione lunga e snervante. Una volta ho impiegato un'intera settimana per correggere un bug dovuto allo scambio di una x con una y.

Questo capitolo sarà più astratto dei precedenti. Vi prego di dirmi se vi è sembrato utile o meno.

6.2 Cosa dovrebbe fare il programma?

La prima cosa da fare (mi sembra ovvio) è pensare a cosa dovrebbe fare il programma se fosse corretto. Iniziate ad eseguire qualche test per vedere che cosa succede. Ad esempio, diciamo che ho scritto un programma che calcola il perimetro di un rettangolo (la somma dei quattro lati) e che ho intenzione di testarne il funzionamento immettendo i seguenti casi:

Larghezza Altezza Perimetro
3 4 14
2 3 10
4 4 16
2 2 8
5 1 12

Ora avvierò il mio programma ed inserirò i dati dei test per vedere se restituisce i risultati che mi aspetto. In caso contrario dovrò scoprire cosa sta facendo il computer.

Comunemente alcuni casi restituiranno il risultato che voglio, mentre altri risulteranno sbagliati, quindi dovrete vedere cos'hanno in comune i casi funzionanti. Ecco ad esempio l'output del programma che calcola il perimetro del rettangolo:

Height: 3
Width: 4
perimeter =  15
Height: 2
Width: 3
perimeter =  11
Height: 4
Width: 4
perimeter =  16
Height: 2
Width: 2
perimeter =  8
Height: 5
Width: 1
perimeter =  8

Il programma restituisce un risultato errato nei primi due casi, corretto nei seguenti due, per sbagliare di nuovo nell'ultimo. Provate ad immaginarvi cos'hanno in comune i casi funzionanti. Quando avrete un'idea sul motivo del malfunzionamento, trovarne la causa sarà più semplice. Con un vostro programma dovreste testare i casi che vi interessano.

6.3 Cosa fa il programma?

La prossima cosa da fare è rileggere il codice sorgente. Una delle cose più importanti da fare mentre si programma è leggere e rileggere il codice sorgente. Innanzitutto analiziamo il codice linea per linea, comportandoci esattamente come farebbe il programma.

Grazie alle strutture di controllo, ovvero i cicli while e le istruzioni if, alcune linee di codice possono non essere eseguite, mentre altre possono essere eseguite più di una volta. L'importante è che immaginiate ad ogni linea il comportamento di Python.

Iniziamo con il semplice programma per calcolare il perimetro. Non scrivetelo, dovete leggerlo e basta. Questo è il sorgente:

height = input("Height: ")
width = input("Width: ")
print "perimeter = ",width+height+width+width

Domanda: Qual'è la prima linea che Python esegue?

Risposta: La prima linea è sempre la prima. In questo caso è: height = input("Height: ").

Domanda: Qual'è il suo effetto?

Risposta: Stampa Height: sullo schermo e attende l'inserimento di un numero da parte dell'utente per assegnarlo alla variabile height.

Domanda: Qual'è la prossima linea che Python esegue?

Risposta: In generale, è la linea successiva, che è width = input("Width: ").

Domanda: Qual'è il suo effetto?

Risposta: Stampa Width:, attende che l'utente inserisca un numero, quindi assegna il valore alla variabile width.

Domanda: Qual'è la prossima linea che eseguirà?

Risposta: La linea successiva è questa: print "perimeter = ",width+height+width+width. Si può anche eseguire la funzione dalla corrente linea, ma lo vedremo meglio nel prossimo capitolo.

Domanda: Qual'è il suo effetto?

Risposta: Per prima cosa stampa perimeter = e poi stamperà width+height+width+width.

Domanda: Questa stringa width+height+width+width calcola il perimetro?

Risposta: Vediamo: il perimetro di un rettangolo si calcola sommando la base (width) più il lato sinistro (height), più il lato superiore (widht), più il lato destro (huh?). L'ultimo elemento, (width) dovrebbe essere il lato destro (height) e non la base (width).

Domanda: È per questo motivo che alcune volte il perimetro risultava corretto?

Risposta: Certo, risultava corretto perché tutti i lati erano uguali.

Nel prossimo programma vedremo come verrà analizzato il codice, supponendo che il programma stampi 5 punti sullo schermo. Dunque, questo è l'output del programma:

. . . .

E questo il programma:

number = 5
while number > 1:
    print ".",
    number = number - 1
print

Questo programma sarà un po' più complesso, perché, come potete notare, nel codice è presente una porzione indentata (o una struttura di controllo). Bando alle ciance, rimbocchiamoci le maniche e cominciamo.

Domanda: Qual'è la prima linea ad essere eseguita?

Risposta: La prima linea del file: number = 5

Domanda: Qual'è il suo effetto?

Risposta: Assegna il valore 5 alla variabile number.

Domanda: Qual'è la prossima linea?

Risposta: La prossima linea è: while number > 1:

Domanda: Qual'è il suo effetto?

Risposta: La struttura while verifica che l'istruzione indentata che segue sia vera o falsa. Nel primo caso esegue il codice indentato sotto, nel secondo caso lo salta per passare al codice indentato al suo stesso livello.

Domanda: Quindi in questo caso come si comporta?

Risposta: Se la condizione number > 1 è vera allora saranno eseguite le prossime due linee.

Domanda: Allora number è maggiore di uno?

Risposta: L'ultimo valore assegnato a number è 5 e quindi cinque è maggiore di uno.

Domanda: Qual'è la prossima linea?

Risposta: Siccome la condizione while è vera, la linea successiva sarà print ".",.

Domanda: Qual'è il suo effetto?

Risposta: Stamperà un punto, e siccome alla fine dell'istruzione compare una virgola (``,''), lo stamperà sulla stessa linea del precedente.

Domanda: Qual'è la prossima linea?

Risposta: number = number - 1 visto che la linea successiva non modifica il livello d'indentazione.

Domanda: Qual'è il suo effetto?

Risposta: Calcola number - 1, dato che il valore corrente di number è 5, gli viene sottratta una unità, creando così il nuovo valore. Sostanzialmente il valore di number viene modificato da 5 a 4.

Domanda: Qual'è la prossima linea?

Risposta: Dato che il livello d'indentazione decrementa, dobbiamo pensare che tipo di struttura di controllo stiamo analizzando. Preso atto che è un ciclo while, dobbiamo tornare alla linea while number > 1:

Domanda: Qual'è il suo effetto?

Risposta: Occorre controllare il valore della variabile number, che è 4 e confrontarla con 1, siccome 4 > 1 il ciclo while prosegue.

Domanda: Qual'è la prossima linea?

Risposta: Siccome il ciclo while è vero, la prossima linea sarà sempre: print ".",

Domanda: Qual'è il suo effetto?

Risposta: Stamperà il secondo punto sulla linea.

Domanda: Qual'è la prossima linea?

Risposta: L'indentazione non è cambiata e quindi: number = number - 1

Domanda: Ed ora cosa succede?

Risposta: Al corrente valore di number (4), sottraiamo 1, che restituisce 3, quindi il nuovo valore assegnato alla variabile number sarà 3.

Domanda: Qual'è la prossima linea?

Risposta: Siccome è verificata la condizione del ciclo while, la linea successiva sarà sempre print ".",.

Domanda: Qual'è la prossima linea?

Risposta: Siccome l'indentazione non è modificata, il che causerebbe l'interruzione del ciclo, la prossima linea sarà sempre: while number > 1:.

Domanda: Qual'è il suo effetto?

Risposta: Compara il valore corrente di number (3) con 1. 3 > 1 perciò il ciclo while continua.

Domanda: Qual'è la prossima linea?

Risposta: Siccome è verificata la condizione del ciclo while, la linea successiva sarà sempre print ".",.

Domanda: Ed ora cosa succede?

Risposta: Un altro punto sulla linea.

Domanda: Qual'è la prossima linea?

Risposta: È sempre: number = number - 1

Domanda: Qual'è il suo effetto?

Risposta: Prende il valore corrente di number (3), gli sottrae 1 e imposta a 2 il nuovo valore.

Domanda: E adesso cosa succede?

Risposta: Si ricomincia da capo: while number > 1:

Domanda: Qual'è il suo effetto?

Risposta: Confronta il valore corrente di number (2) con 1. Siccome 2 > 1 il ciclo while continua.

Domanda: Qual'è la prossima linea?

Risposta: Il ciclo while prosegue: print ".",

Domanda: Qual'è il suo effetto?

Risposta: Scoprirete il senso della vita, l'universo sarà vostro. Sto scherzando (così sono sicuro che vi sveglierete). Stampa il quarto punto sullo schermo.

Domanda: Qual'è la prossima linea?

Risposta: È: number = number - 1

Domanda: Qual'è il suo effetto?

Risposta: Prende il valore corrente di number (2) gli sottrae 1 e imposta ad 1 il nuovo valore.

Domanda: Qual'è la prossima linea?

Risposta: Torniamo al ciclo while: while number > 1:

Domanda: Qual'è il suo effetto?

Risposta: Compara il corrente valore di number (1) con 1. Siccome 1 > 1 è falso (uno non è più grande di uno), il ciclo while termina.

Domanda: Qual'è la prossima linea?

Risposta: Siccome la condizione del ciclo while è falsa, la prossima linea analizzata, dopo l'uscita dal ciclo, sarà print.

Domanda: Qual'è il suo effetto?

Risposta: Stampa sullo schermo un'altra linea, vuota.

Domanda: Ma il programma non doveva stampare 5 punti?

Risposta: Il ciclo termina troppo presto e non stampa 1 punto.

Domanda: Come posso riparare questo difetto?

Risposta: Creare un ciclo che termina dopo aver stampato l'ultimo punto.

Domanda: E come posso realizzarlo?

Risposta: Ci sono molte possibilità. Una via è modificare il ciclo così: while number > 0:. Un'altra è questa: number >= 1. Esistono molti altri metodi.

6.4 Come posso riparare ad un errore nel programma?

Dovete capire cosa sta facendo il programma. Immaginarvi cosa dovrebbe fare il programma, confrontare e capire le differenza dei metodi che provate. Il Debugging si impara grazie all'esperienza. Se non riuscite a trovare il difetto dopo un'ora o due, fate una pausa e parlatene con qualcuno. Quando ci ritornerete sopra probabilmente avrete nuove idee per risolvere il problema. Buona fortuna.