En Perl, el ciclo foreach permite recorrer fácilmente los elementos de un arreglo, y la variable del ciclo por defecto ($_, o una que se especifique explícitamente) apunta al elemento del arreglo, en lugar de copiar su valor.
Ejemplo:
my @arr = (1, 2, 3); foreach my $x (@arr) { $x *= 2; } # @arr es (2, 4, 6) — ¡los elementos han sido modificados!
Para obtener una copia del valor (y no una referencia al elemento original), es necesario asignar la variable explícitamente:
foreach my $x ( @arr ) { # $x es una referencia al elemento ... } # ↓↓↓↓ foreach (@arr) { my $copy = $_; ... } # $copy es una copia, $_ es una referencia al elemento
No se debe usar la misma variable en múltiples ciclos foreach anidados, de lo contrario, pueden ocurrir efectos secundarios inesperados.
¿Es la variable en el ciclo foreach una copia independiente, y puede modificarse de forma segura dentro del cuerpo del ciclo?
Normalmente se responde: "Sí, porque el ciclo crea la variable automáticamente — se puede cambiar lo que se quiera."
Respuesta correcta:
En Perl, la variable del ciclo por defecto (por ejemplo, foreach $var (@arr)) es un alias (referencia) al elemento del arreglo. ¡Cualquier cambio en la variable del ciclo resultará en un cambio en el elemento original!
Para modificar una copia temporal, haz una asignación explícita dentro del ciclo:
foreach my $elem (@arr) { my $t = $elem; $t++; # solo la copia, el arreglo original no cambia }
Historia
Historia 1
En una tarea de filtrado de registros, un programador modificó la variable de su ciclo, sin sospechar que estaba cambiando los datos originales. Los cálculos posteriores se realizaron sobre el arreglo modificado, lo que resultó en un cálculo incorrecto de la suma de los elementos. El error solo se manifestó al compararlo con un conjunto de referencia.
Historia 2
Un foreach anidado utilizó la misma variable ($item). La variable del ciclo interno sobrescribió el estado de la externa, lo que resultó en la pérdida de los resultados de la primera pasada, y parte del arreglo terminó con valores incorrectos.
Historia 3
Un arreglo de referencias a hashes se recorrió a través de foreach, y en cada ciclo se asignaba un nuevo valor a la variable del ciclo. Como resultado, las referencias se "rompieron" — en lugar de valores referenciales ahora había escalares, y la siguiente etapa del procesamiento falló con un error de tipo.