Tutorial
6
Scritto da Roberto Navigli (roberto.navigli@iol.it)
Game Programming Italia: www.gpi.eden.it
Istruzioni
di controllo - seconda parte
Il
concetto di iterazione è uno dei pilastri della
programmazione imperativa. Ripetere un blocco di istruzioni
finché sono mantenute determinate condizioni
è di importanza fondamentale nella maggior
parte dei programmi.
L'istruzione
while
Uno
dei modi di realizzare l'iterazione è quello
di utilizzare l'istruzione while. Essa permette di
ripetere l'esecuzione del blocco di istruzioni finché
è vera l'espressione indicata tra parentesi
subito dopo il while:
while(espressione
di test) istruzioni
dove
istruzioni può essere una singola istruzione
oppure un blocco di istruzioni racchiuso da parentesi
graffe. Ad esempio:
int
n = 100;
while(n--) cout << n << " ";
Questo
frammento stampa tutti i numeri da 99 a 0 separati
da uno spazio. Soffermiamoci per un momento sull'espressione:
n--
Questa
espressione vale n ma essa include anche l'esecuzione
di un'operazione di decremento, dovuta all'operatore
--. Quindi il valore dell'espressione è n,
ma la variabile n verrà subito dopo decrementata
di 1. Si controlla che il valore di n (100) sia diverso
da 0, si decrementa n, quindi si esegue l'istruzione
di stampa, si controlla che il valore di n (99) sia
diverso da 0, si decrementa n e si esegue la stampa
e così via finché n non vale 0. A quel
punto l'iterazione si interrompe.
Vediamo
ora un esempio completo:
#include
<iostream>
using
namespace std;
void
main()
{
char c = 0;
while(c != 'x')
{
cout << "Inserisci un carattere (x per
uscire) e premi invio: ";
cin >> c;
cout << "Hai inserito il carattere: "
<< c << endl;
}
}
Nel
main viene dichiarata una variabile di tipo carattere
e inizializzata a zero,
char
c = 0;
quindi
si richiede l'inserimento di un carattere (memorizzato
nella variabile c) ripetutamente finché c non
è uguale al carattere 'x' (i caratteri sono
indicati tra apici).
L'istruzione
do ... while
Questa
istruzione è simile al while, con la differenza
che la condizione è posta alla fine del blocco
di istruzioni (o della singola istruzione). La forma
generale è:
do
istruzioni
while(espressione di test);
Ad
esempio:
int
n = 100;
do
cout << n << " ";
while(n--);
Stampa
tutti i numeri da 100 a 0. Infatti il decremento della
variabile n avviene solo dopo la stampa della stessa.
L'utilità del do ... while rispetto all'istruzione
while è che a volte può essere utile
eseguire il blocco di istruzioni almeno una volta.
Vediamo ancora un esempio:
int
n = 0;
//
tutti i numeri pari da 0 a 100
do
{
// stampa la variabile
cout << n << " ";
// incrementa di due la variabile n
n += 2;
} while(n <= 100);
L'istruzione
for
Spesso
le iterazioni sono basate su contatori o variabili
che determinano la validità della condizione
di "test", come si è visto anche
negli esempi precedenti. La forma generale del for
è la seguente:
for
(inizializzazione; espressione; aggiornamento) istruzioni
Ci
sono tre campi che determinano il tipo di iterazione:
l'inizializzazione, l'espressione di test e l'aggiornamento.
La sezione di inizializzazione assegna il valore iniziale
alla variabile che controlla l'iterazione. Essa viene
eseguita una volta sola prima che abbia inizio il
ciclo. La seconda sezione contiene l'espressione di
test, ovvero quell'espressione che determina il proseguimento
o la definitiva interruzione dell'iterazione, secondo
se essa è vera oppure falsa rispettivamente.
La condizione di test viene verificata all'inizio
di ogni esecuzione del blocco di istruzioni. La terza
sezione contiene l'istruzione di aggiornamento della
variabile che controlla il ciclo. Tale istruzione
viene eseguita al termine di ciascuna esecuzione del
blocco di istruzioni.
Vediamo subito un esempio:
#include
<iostream>
using
namespace std;
void
main()
{
int numero;
for (numero = 1; numero <= 15; numero++)
cout << numero << " ";
}
Nel
for dell'esempio, l'intero numero viene inizializzato
a 1. La condizione che determina il proseguimento
del ciclo è:
numero
<= 15
e
ad ogni ciclo la variabile viene incrementata di uno
mediante l'istruzione:
numero++
Il
programma stampa i primi 15 interi a partire da 1:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15
Vediamo
ancora in dettaglio l'ordine con cui vengono eseguite
le diverse sezioni del for.
Per prima cosa viene inizializzata la variabile numero
a 1. Quindi viene controllato che numero sia minore
o uguale a 15. A questo punto viene eseguito il corpo
del for, ovvero viene stampato il valore della variabile
numero, che è pari a 1. Quindi essa viene incrementata
di 1. Viene controllato che numero sia minore o uguale
a 15, viene stampato il valore 2 e numero è
di nuovo incrementato di 1 e così via finché
numero non assume il valore 16. A quel punto, quando
si effettua il controllo sulla condizione, l'iterazione
termina.
Così
come nel while, poiché il test viene eseguito
prima delle istruzioni specificate nel corpo del for,
è possibile che il ciclo non venga eseguito
neanche una volta. Ad esempio:
#include
<iostream>
using
namespace std;
void
main()
{
int numero = 20;
for (; numero <= 15; numero++) cout << numero
<< " ";
cout << "Nessun numero stampato!"
<< endl;
}
Compilando
ed eseguendo questo programma, nessun numero verrà
stampato. Si otterrà in output la sola stringa:
Nessun
numero stampato!
Questo
avviene perché la condizione
numero
<= 15
viene
controllata prima dell'esecuzione delle istruzioni
che costituiscono il corpo del for.
In questo esempio, la sezione di inizializzazione
è vuota. Ciascuna sezione del for può
essere vuota. Ci si chiederà quale utilità
può avere un for senza l'istruzione di incremento
o senza la condizione di test. In realtà esse
possono essere inserite all'interno del blocco di
istruzioni. Ad esempio:
//
inizializzazione
int n = 0;
for
(; n < 10; )
{
cout << n << " ";
// aggiornamento della variabile di controllo
n += 2;
}
Questo
frammento stampa i numeri pari da 0 a 8:
0
2 4 6 8
La
tecnica di incrementare la variabile nelle istruzioni
che costituiscono il corpo del for torna utile quando
questo incremento è condizionato da altri eventi
o quando esso deve avvenire a metà del blocco
di istruzioni. Il caso estremo di for con nessuna
delle tre sezioni specificate è quello del
for infinito:
for
(;;)
{
// queste istruzioni vengono eseguite all'infinito
}
che
è equivalente a:
while(true)
{
// queste istruzioni vengono eseguite all'infinito
}
Le
istruzioni vengono eseguite all'infinito, poiché
la condizione di test è sempre vera.
Può
capitare inoltre che il corpo del for sia vuoto, ad
esempio:
int
n;
for (n = 0; n < 100; n++);
Finora
abbiamo considerato il caso in cui ci sia una sola
variabile di controllo. In realtà l'istruzione
for ammette la presenza di più variabili di
controllo. Ad esempio:
int
a, b;
for (a = 0, b = 2; a*2 <= b; a++, b++)
cout << "a = " << a <<
", b = " << b << endl;
Esaminiamo
i singoli passi:
inizializzazione:
a = 0, b = 2
test: 0*2 <= 2 cioè 0 <= 2 ? sì
istruzione: stampa "a = 0, b = 2"
aggiornamento: a = 1; b = 3;
test: 1*2 <= 3 cioè 2 <= 3 ? sì
istruzione: stampa "a = 1, b = 3"
aggiornamento: a = 2, b = 4
test: 2*2 <= 4 cioè 4 <= 4 ? sì
istruzione: stampa "a = 2, b = 4"
aggiornamento: a = 3, b = 5
test: 3*2 <= 5 cioè 6 <= 5 ? no, esce
Le
istruzioni break e continue
Parlando
del for, abbiamo visto che è possibile omettere
la condizione di test. Ma come è possibile
allora uscire dall'iterazione? L'istruzione break
ha proprio questa funzione. Ad esempio:
int
x;
for (;;)
{
cout << "Inserisci un numero (0 per uscire):
";
// richiede un numero in input
cin >> x;
// se x == 0, esci dal ciclo
if (!x) break;
}
Nell'esempio,
se x è uguale a zero, si esegue l'istruzione
break che esce dal ciclo, altrimenti si continua a
chiedere un numero all'utente. Chiaramente questo
frammento poteva anche essere scritto così:
int
x;
for (; x;)
{
cout << "Inserisci un numero (0 per uscire):
";
cin >> x;
}
O
anche:
int
x;
while(x)
{
cout << "Inserisci un numero (0 per uscire):
";
cin >> x;
}
Ma
in generale questa equivalenza non è sempre
possibile, perché il codice può essere
molto più complicato e i break possono essere
più d'uno.
L'istruzione
continue permette di interrompere una iterazione senza
completarla per passare direttamente alla successiva.
Ad esempio:
for
(int x = 0; x < 10; x++)
{
// se x è dispari passa all'iterazione successiva
if (x % 2) continue;
cout << x << " è un numero
pari!" << endl;
}
Quando
l'espressione x % 2 è vera (cioè quando
x % 2 è uguale a uno, ovvero x è dispari)
si passa all'iterazione successiva e non viene emesso
il messaggio in output.
L'istruzione goto
L'istruzione
goto realizza un concetto molto semplice della programmazione
imperativa: vai a questo punto del codice. La forma
generica del goto è la seguente:
goto
etichetta;
Dove
l'etichetta è presente in un punto del programma.
Ma che cos'è un'etichetta? E' una sorta di
segnaposto che individua un punto specifico di un
programma. Quando si specifica l'etichetta, essa deve
essere seguita dai due punti. Vediamo un esempio:
#include
<iostream>
using
namespace std;
void
main()
{
int x;
cout << "Inserisci un numero: " <<
endl;
cin >> x;
if (x > 10) goto maggiore;
cout << "Hai inserito un numero <= 10!"
<< endl;
goto esci;
maggiore:
cout << "Hai inserito un numero > 10!"
<< endl;
esci:
cout << "Goto? No grazie" <<
endl;
}
L'istruzione
goto è stata bistrattata da quando Dijkstra
pubblicò l'articolo "Goto considered harmful".
In effetti, si può sempre fare a meno del goto,
sostituendolo con le altre istruzioni di controllo.
Avremmo potuto scrivere l'esempio precedente senza
goto e più elegantemente:
#include
<iostream>
using
namespace std;
void
main()
{
int x;
cout << "Inserisci un numero: ";
cin >> x;
if (x > 10) cout << "Hai inserito un
numero > 10!" << endl;
else cout << "Hai inserito un numero <=
10!" << endl;
}
L'uso
del goto è oggi limitato a quelle applicazioni
real-time che hanno bisogno di garantire in alcuni
punti la massima efficienza possibile o a sorgenti
generati da programmi come ad esempio i generatori
di parser.
|