Break e continue - Refactoring
Uno degli scopi del testing automatico, quello che in queste esercitazioni viene realizzato per mezzo di pvcheck
, consiste nella possibilità di partire da un programma funzionante e di farne il refactoring.
Fare il refactoring di un programma significa "modificare la struttura interna di porzioni di codice senza modificarne il comportamento esterno".
Il refactoring di un programma viene fatto per vari motivi: per semplificare il codice, magari accorciandone la lunghezza sintetizzando gli algoritmi, per migliorarne la chiarezza, per esempio cambiando nomi a variabili e funzioni, ecc.
La pagina Wikipedia sul refactoring può essere un utile punto di partenza per approfondire questo importante aspetto del moderno sviluppo software.
Il testing automatico permette di verificare che un programma il cui codice è stato rifattorizzato fornisca lo stesso output del programma originale, cosa che garantisce (entro certi limiti) che il refactoring non abbia introdotto degli errori nel codice.
Break e continue
Il programma (break-continue.c), di cui si riporta la parte rilevante della funzione main
, rappresenta una versione semplificata del programma riportato nel libro di testo (vedi S4.10) per spiegare l'uso delle istruzioni break
e continue
:
puts("[OUTPUT]");
do {
fgets(s, sizeof(s), stdin);
n = atoi(s);
if (n < 0) {
printf("%d < 0: uscita dal loop...\n", n);
break;
}
if (n > 10) {
printf("%d > 10: non ammesso\n", n);
continue;
}
/* utilizza il valore di n */
printf("Valore immesso: n = %d\n", n);
} while (1);
puts("Programma terminato");
Il programma esegue un ciclo do-while
infinito, che ad ogni iterazione, legge un valore numerico da tastiera traminte fgets
e atoi
. A seconda del valore letto e delle condizioni presenti nel programma, vengono stampati a video dei messaggi, ed eventualmente il programma termina.
Si compili il programma con il comando
gcc -Wall break-continue.c
Se non ci sono errori di compilazione, si può eseguire il programma a.out
generato per verificarne la correttezza.
Un esempio di ciò che avviene nel terminale durante l'esecuzione del programma è il seguente:
$ ./a.out
[OUTPUT]
5
Valore immesso: n = 5
50
50 > 10: non ammesso
-500
-500 < 0: uscita dal loop...
Programma terminato
dove
- la riga
$ ./a.out
è il comando eseguito per lanciare il programma (include il prompt$
, che non fa parte del comando da impartire); - le linee che riportano i valori
5
,50
e-500
sono inserite da tastiera dall'utente; - il resto è stampato a video dal programma.
Il contenuto del terminale riportato sopra si ottiene lanciando il programma e inserendo, tra un output e l'altro, i tre numeri 5
, 50
e -500
, in questo ordine.
La stampa di [OUTPUT]
è necessaria per poter far valutare il testo stampato tramite pvcheck
.
Verifica automatica
Il programma può essere testato utilizzando il file di test break-continue.test, ed eseguendo il comando:
pvcheck -f break-continue.test ./a.out
Si ricordi che il file break-continue.test
deve essere presente nella directory nella quale si sta lanciando pvcheck
per testare il programma a.out
precedentemente ottenuto con la compilazione di break-continue.c
.
L'opzione -f
seguita dal nome del file di test è necessaria in quanto pvcheck
cerca normalmente un file di test chiamato pvcheck.test
, e se non lo trova, termina con un errore.
Per utilizzare un file di test diverso da pvcheck.test
si una l'opzione -f
per specificarne il nome.
Equivalenza tra programmi
Per il Teorema di Jacopini-Bohm, le strutture fondamentali della programmazione strutturata sono sufficienti per realizzare qualsiasi algoritmo.
Quindi che le strutture "base" del linguaggio C (strutture condizionali e iterative) sono sufficienti per realizzare il programma break-continue.c
.
Richiesta
Fare il refactoring del programma break-continue.c
in modo da eliminare l'uso sia di break
che di continue
.
Il nuovo programma dovrà essere equivalente a quello di partenza, ovvero dovrà produrre lo stesso risultato (il testo stampato a video) a fronte di qualsiasi sequenza di valori inseriti da tastiera.
L'equivalenza del nuovo programma con quello esistente può essere verificata per mezzo di pvcheck
: se i test applicati al nuovo programma vengono superati tutti, significa che (entro i limiti di verifica dei test) il nuovo programma è equivalente a quello originale.
Suggerimenti
Per ottenere un programma equivalente eliminando l'uso di break
e continue
bisogna introdurre una condizione opportuna di terminazione del ciclo do-while
, che quindi non potrà più essere un ciclo infinito.
Inoltre, sarà necessario riorganizzare opportunamente le istruzioni condizionali if
all'interno del corpo del ciclo, eventualmente introducendo se necessario delle clausole else
.