Corso di Perl

Strutture di controllo: ancora Foreach

 

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 all’altra 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 dell’array 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, dall’altra è confrontata con 0), perciò il valore che produce (come dovreste ricordarvi) è la lunghezza dell’array.

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 d’uscita?

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 l’array 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 l’elemento corrente dell’array. Per spiegare questo lungo giro di parole:

foreach $valore (@valori)
      {
      $valore *= 2;
      }


L’effetto è di raddoppiare tutti i valori contenuti in @valori! Questo perché la variabile $valore non ha un’esistenza 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