In Perl, il ciclo foreach consente di iterare comodamente sugli elementi di un array, dove la variabile di ciclo per impostazione predefinita ($_, oppure esplicitamente specificata) fa riferimento all'elemento dell'array, anziché copiare il suo valore.
Esempio:
my @arr = (1, 2, 3); foreach my $x (@arr) { $x *= 2; } # @arr è (2, 4, 6) — gli elementi sono stati modificati!
Per ottenere esattamente una copia del valore (e non un riferimento all'elemento originale), è necessario assegnare esplicitamente la variabile:
foreach my $x ( @arr ) { # $x è un riferimento all'elemento ... } # ↓↓↓↓ foreach (@arr) { my $copy = $_; ... } # $copy è una copia, $_ è un riferimento all'elemento
Non si dovrebbe usare la stessa variabile in più cicli foreach annidati, altrimenti si possono ottenere effetti collaterali inattesi.
La variabile nel ciclo foreach è una copia indipendente e può essere modificata in modo sicuro all'interno del corpo del ciclo?
Di solito si risponde: "Sì, poiché il ciclo crea da solo la variabile — si può cambiare come si vuole."
Risposta corretta:
In Perl, la variabile di ciclo per impostazione predefinita (ad esempio, foreach $var (@arr)) è un alias (riferimento) all'elemento dell'array. Qualsiasi modifica alla variabile di ciclo porta a una modifica dell'elemento originale!
Per modificare una copia temporanea — eseguire un'assegnazione esplicita all'interno del ciclo:
foreach my $elem (@arr) { my $t = $elem; $t++; # solo copia, l'array originale non viene modificato }
Storia
Storia 1
In un compito di filtraggio dei record, un programmatore ha modificato la variabile del proprio ciclo, senza sapere di star cambiando i dati originali. I calcoli successivi si sono svolti sull'array modificato, il che ha portato al calcolo errato della somma degli elementi. L'errore è emerso solo nel confronto con un campione di riferimento.
Storia 2
Un foreach annidato utilizzava la stessa variabile ($item). La variabile del ciclo interno ha sovrascritto lo stato di quella esterna, portando alla perdita dei risultati del primo passaggio, mentre parte dell'array ha avuto valori errati.
Storia 3
Un array di riferimenti a hash è stato iterato tramite foreach, e in ogni ciclo veniva assegnato un nuovo valore alla variabile di ciclo. Di conseguenza, i riferimenti sono stati "romperti" — invece dei valori di riferimento ora c'erano scalar, e la fase successiva del trattamento è fallita con un errore di tipo.