Nella
lezione 14 abbiamo visto un assaggio del ciclo For, di sicuro
il più utilizzato nell'ambito dei cicli disponibili
in Visual Basic: questo ciclo è identificato da due
istruzioni, poste rispettivamente all'inizio e alla fine del
ciclo:
For
contatore = inizio To fine
[istruzioni]
Next contatore |
Contatore
indica una variabile numerica che "conta" quante
volte devono essere eseguite le istruzioni comprese nel ciclo:
essa assume un valore iniziale (inizio) e viene incrementata
dall'istruzione Next fino a raggiungere il valore finale (fine).
Nella sintassi standard, le istruzioni contenute all'interno
del ciclo sono eseguite fintantoché contatore risulta
minore o uguale a fine: ciò significa che, quando il
ciclo termina, la variabile contatore ha un valore pari a
fine + 1, perché nell'ultima esecuzione del ciclo risulta
contatore = fine e l'istruzione Next incrementa ulteriormente
il contatore.
Questa è una caratteristica tipica dei cicli "For",
che si riscontra anche negli altri linguaggi di programmazione:
e non potrebbe essere altrimenti, in quanto l'esecuzione del
ciclo termina proprio quando contatore supera il valore fine.
Nella sintassi standard appena descritta, l'incremento del
contatore è di una unità ad ogni esecuzione
del ciclo, ma è possibile modificare il valore dell'incremento
usando la clausola Step dopo il valore fine:
For
contatore = inizio To fine Step incremento
[istruzioni]
Next contatore |
All'inizio
del ciclo il valore del contatore è uguale a inizio,
alla seconda esecuzione del ciclo è uguale a inizio+incremento,
alla terza esecuzione è uguale a inizio+incremento+incremento
e così via fino a che il contatore diventa maggiore
del valore fine. Il valore di incremento viene sommato algebricamente
al valore corrente del contatore: ciò significa che
l'incremento può anche essere negativo, determinando
così una diminuzione progressiva del valore del contatore:
in questo caso il ciclo cesserà di essere eseguito
quando il contatore diventa minore del valore finale specificato.
Perché il ciclo funzioni correttamente, quindi, il
valore iniziale dovrà essere maggiore del valore finale,
ad es. il ciclo seguente viene eseguito tre volte, per i=10,
i=8, i=6; all'uscita dal ciclo sarà i=4:
Dim
i As Integer 'contatore
For i = 10 To
5 Step -2
'[istruzioni]
Next i |
Si
è detto che il contatore è una variabile numerica,
senza specificare il suo tipo: infatti, benché esso
sia solitamente un Integer, è possibile anche utilizzare
valori frazionari come Single o Double, e lo stesso vale per
i valori inizio, fine e incremento. Tuttavia, è sempre
preferibile utilizzare valori interi per motivi di precisione:
le variabili di tipo Single o Double, infatti, commettono
errori di precisione che, per quanto piccoli, non sono affatto
trascurabili; basta un errore di un milionesimo per far credere
a Visual Basic che il valore finale sia stato superato e determinare
anzitempo la fine del ciclo. Si consideri ad esempio il ciclo
seguente:
Dim
i As Single
For i = 0.6 To
0.8 Step 0.1
Debug.Print i
Next i
Debug.Print i |
In
teoria esso dovrebbe essere eseguito per tre volte, con il
contatore i che vale 0.6, 0.7 e 0.8; invece esso è
eseguito soltanto due volte, per i = 0.6 e i = 0.7: quando
i viene ulteriormente incrementato non vale 0.8, bensì
0.8000001, che essendo maggiore di 0.8 determina l'interruzione
del ciclo. Per questo motivo è sempre buona norma usare
valori interi sia per il contatore che per i valori di inizio,
fine e incremento, in quanto con valori interi il microprocessore
non commette quegli errori di imprecisione che possono determinare
comportamenti anomali.
Naturalmente i valori iniziale e finale del contatore, ovvero
il numero di iterazioni, non devono necessariamente essere
stabiliti esplicitamente durante la scrittura del codice:
essi possono essere determinati durante l'esecuzione del programma
semplicemente utilizzando delle variabili aggiuntive: ad esempio,
il programma potrebbe chiedere all'utente quali debbano essere
i valori di inizio e fine del contatore e memorizzare questi
valori in due variabili da usare nell'istruzione For:
Dim
i As Integer 'contatore
Dim intInizio As
Integer, intFine As Integer
'valori iniziale e finale
intInizio=Val(InputBox("Inserisci il valore iniziale
del contatore:"))
intFine=Val(InputBox("Inserisci il valore finale
del contatore:"))
For i = intInizio To
intFine
Debug.Print i
Next i |
In
questo esempio si è fatto uso della funzione InputBox,
che richiede all'utente l'inserimento di un valore in una
casella di testo: tale valore è quello restituito dalla
funzione, e poiché è di tipo stringa esso deve
essere convertito in numero attraverso la funzione Val. È
chiaro che se l'utente inserisce nella casella un valore che
non può essere convertito in numero (ad es. "a56bc"),
sarà generato un errore causato dalla funzione Val.
Come per altri blocchi di istruzioni, anche per i blocchi
For
Next è possibile usare la nidificazione, ovvero
includere un blocco For all'interno di un altro blocco For:
For
i = 1 To 10
For k = 5 To
22
'[istruzioni]
Next k
Next i |
La
cosa importante è non intersecare mai due cicli
distinti, ovvero non fare mai una cosa del genere:
For
i = 1 To 10 'inizio
PRIMO ciclo
For k = 5 To
22 'inizio SECONDO ciclo
'[istruzioni]
Next i 'fine
PRIMO ciclo
Next k 'fine
SECONDO ciclo |
Così
facendo si genera un errore di sintassi, perché l'istruzione
"Next i" non corrisponde all'istruzione "For
k" che la precede: il secondo ciclo non è interamente
contenuto nel primo, ma si sovrappone ad esso generando confusione.
Volendo, è possibile anche omettere il nome del contatore
nell'istruzione Next, e in tal caso Visual Basic assumerà
automaticamente che esso si riferisce all'istruzione For immediatamente
precedente
For
i = 1 To 10
For k = 5 To
22
'[istruzioni]
Next '
Visual Basic assume che l'istruzione sia: Next k
Next '
Visual Basic assume che l'istruzione sia: Next i |
Spesso
è necessario interrompere l'esecuzione di un ciclo
se si verifica una determinata condizione: Visual Basic mette
a disposizione del programmatore un'istruzione apposita: Exit
For, che sposta il punto di esecuzione del programma all'istruzione
immediatamente successiva all'istruzione Next relativa al
ciclo interrotto. Solitamente questa istruzione si fa dipendere
da un blocco If che controlla il verificarsi di una "condizione
di uscita":
For
i = 1 To 10
If i = 8 Then
Exit For
Debug.Print i
Next i
Debug.Print "ciclo interrotto" |
Il
ciclo precedente, che semplicemente visualizza il valore del
contatore, teoricamente dovrebbe essere eseguito dieci volte
ma in realtà è eseguito solo otto volte, perché
all'ottava esecuzione la condizione i = 8 risulta verificata
determinando l'uscita dal ciclo (senza stampare il valore
del contatore) e la visualizzazione del messaggio "ciclo
interrotto"; si noti che questo messaggio sarebbe visualizzato
in ogni caso, al termine del ciclo.
I "puristi" della programmazione non vedono di buon
occhio l'utilizzo dell'istruzione Exit For, perché
in questo caso il ciclo non termina solo con l'ultima istruzione
"Next", come ci si potrebbe aspettare in condizioni
normali, ma potrebbe terminare anche all'interno del ciclo
stesso: ciò determina un'eccezione alla normale esecuzione
del codice e pertanto potrebbe complicarne la comprensione
e la modifica.
In effetti utilizzare l'istruzione Exit For non è l'unico
modo per interrompere un ciclo: sapendo che esso termina quando
il contatore supera il valore finale, la soluzione più
intuitiva sarebbe quella di assegnare al contatore un valore
maggiore del limite finale in modo che il ciclo non prosegua
oltre:
For
i =1 To 10
If i = 8 Then
i =11
Debug.Print i
Next i
Debug.Print "ciclo interrotto" |
Nell'esempio
precedente, quando i=8 il programma assegna al contatore il
valore 11, cioè un valore superiore al limite di 10
specificato nell'istruzione For; in questo modo il ciclo non
è interrotto direttamente, e infatti l'istruzione Debug.Print
i è eseguita normalmente anche quando i=8, però
l'istruzione Next i verifica che il contatore ha superato
il limite e quindi determina la fine del ciclo e la visualizzazione
del messaggio "ciclo interrotto".
Tuttavia, da un punto di vista stilistico questa soluzione
è ancora peggiore della precedente, perché il
valore del contatore non dovrebbe mai essere modificato direttamente
all'interno del ciclo stesso: un metodo del genere può
complicare molto il debug e l'eventuale modifica del codice
perché può generare errori imprevisti e generalmente
difficili da identificare.
|