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.