Riprendiamo ancora una volta in mano il nostro programma delle
altezze, e in particolare la parte che esegue il calcolo vero e
proprio. Dopo aver spiegato il ciclo while, avevamo scritto questo:
$media=0;
while(($chiave, $valore) = each( %altezza))
{
$media = $media + $valore;
}
$media = $media / keys(%altezza) if keys(%altezza) != 0;
Che non è male, ma ha ancora qualche difetto: per prima cosa, stiamo
usando each, che richiede particolare attenzione: come sicuramente
(vero?) ricorderete, il risultato di each dipende da quanti
each sono stati eseguiti in precedenza. Questo per adesso
non è un problema, ma il segmento di codice qui sopra non si può
spostare da una parte allaltra del programma, se ce ne fosse
la necessità: il suo each deve essere sempre il primo ad
essere eseguito, altrimenti la somma non torna.
Possiamo sfruttare il ciclo foreach, e la variabile $_ per
scrivere questo:
$media=0;
foreach (values(%altezza))
{
$media += $_;
}
$media /= keys(%altezza) if keys(%altezza) != 0;
Come sapete, values() produce un array che contiene tutti
i valori dellarray associativo %altezza. Questi valori
vengono passati uno alla volta a $_, e quindi sommati dentro $media.
Anche keys() dovrebbe produrre un array... ma come sapete,
nel Perl i risultati dipendono dal contesto, scalare o array. Qui
keys() è usata in un contesto evidentemente scalare (da una
parte fa da divisore, dallaltra è confrontata con 0), perciò
il valore che produce (come dovreste ricordarvi) è la lunghezza
dellarray.
Se i segni += e /= vi stanno confondendo
le idee, allora vuol dire che non sapete programmare in C :-) Si
tratta di una forma abbreviata per modificare il valore di una variabile.
Scrivere:
$a += $b;
è come scrivere:
$a = $a + $b;
ma è più veloce. Quasi tutti gli operatori (le quattro operazioni,
la concatenazione di stringa ., eccetera) possono essere
scritti in questo modo.
Foreach abbreviato
Come if e while, anche foreach ha la sua forma
abbreviata: otteniamo così un codice ancora più compatto, ma sempre
leggibile:
$media=0;
$media += $_ foreach values(%altezza);
facendo la somma di tutti i valori di un array in sole due righe.
E le vie duscita?
Come per tutti i cicli Perl, anche per i cicli for e foreach
si possono applicare last, next e redo con
gli stessi identici significati che avevano nel ciclo while:
$media=0;
foreach (values(%altezza))
{
next if $_ <0;
$media += $_;
}
Il next inserito nel blocco esegue un controllo addizionale
sulle altezze, eliminando dal conto quelle che sono minori di zero
(cosa impossibile). Potremmo anche decidere che un evento del genere
vanifica tutto il calcolo:
$media=0;
foreach (values(%altezza))
{
last if $_ <0;
$media += $_;
}
In questo caso, alla prima altezza negativa il ciclo esce. Va detto,
però, che questi due esempi non sono un granché come controllo sugli
errori: il programma che segue non ha modo di verificare se il valore
contenuto in $media è valido oppure no. Potremmo fare una
cosa del genere:
$media=0;
foreach (values(%altezza))
{
if ($_ < 0)
{
$media = -1;
last;
}
$media += $_;
}
perché, come vi ripeto, last e i suoi compagni agiscono sul
ciclo più vicino anche se sono inseriti dentro le parentesi di un
if più interno. Il resto del programma dovrà poi controllare
il valore di $media, e scrivere un messaggio di errore opportuno
se questa è uguale a -1.
Attenzione!
Cè un aspetto di foreach che può essere molto utile, ma anche
generare molta confusione. Se scrivete esplicitamente la variabile
iterativa, e se larray compreso tra parentesi
è un array vero e proprio e non una qualche lista generata in un
modo strano, quando il blocco viene eseguito la variabile diventa
un sinonimo per lelemento corrente dellarray. Per spiegare
questo lungo giro di parole:
foreach $valore (@valori)
{
$valore *= 2;
}
Leffetto è di raddoppiare tutti i valori contenuti in @valori!
Questo perché la variabile $valore non ha unesistenza indipendente,
ma viene usata di volta in volta come sinonimo per $valori[0], $valori[1],
eccetera. Tutto ciò, come dicevo prima, può tornare molto utile
oppure essere la causa di errori incomprensibili, perciò occhio.
Nella prossima lezione vedremo altri tipi di cicli (eh sì, ce ne
sono tanti!).
Torna all'indice Generale del corso di Corso di Perl di Software Planet