2010-07-09 17 views
15

Estoy escribiendo un script en Perl y tengo una pregunta sobre la construcción de foreach de Perl.Foreach de Perl y cambio de la variable de bucle

Parece que si cambia una de las variables de bucle cambia en la matriz real. ¿Es este el caso, o he hecho algo completamente mal?

Quiero cambiar una cadena como abc.abc#a a abc_abc_a (guiones bajos para caracteres no alfanuméricos), pero tengo que conservar el valor original en la matriz para su uso posterior.

tengo código que se ve algo como esto:

@strings = ('abc.abc#a', 'def.g.h#i'); 
foreach my $str (@strings){ 
    $str =~ s/[^0-9A-Za-z]/_/g; 
    print $str, "\n"; #Actually I use the string to manipulate files. 
} 

que podría resolver el problema de la siguiente manera:

@strings = ('abc.abc#a', 'def.g.h#i'); 
foreach my $str (@strings){ 
    my $temp = $str; #copy to a temporary value 
    $temp =~ s/[^0-9A-Za-z]/_/g; 
    print $temp, "\n"; #$str remains untouched... 
} 

pero hay una manera más eficiente de lograr esto?

Muchas gracias!

Respuesta

16

No estás loco; este es un comportamiento normal. Ver perldoc perlsyn bajo foreach:

Si ningún elemento de lista es un valor-I, se puede modificar mediante la modificación de VAR dentro del bucle . Por el contrario, si algún elemento de LIST no es un lvalue, cualquier intento de modificar ese elemento fallará. En otras palabras, la variable de índice de bucle "foreach" es un alias implícito para cada elemento de la lista sobre la que está pasando el bucle.

Otros iteradores lazo como la map tienen un comportamiento similar:

map BLOCK LIST 
map EXPR,LIST 

...
Tenga en cuenta que $ _ es un alias para el valor de la lista, por lo que se pueden utilizar para modificar el elementos de la LISTA. Si bien esto es útil y es compatible, puede ocasionar resultados extraños si los elementos de LIST no son . El uso de un bucle "foreach" regular para este propósito sería más claro en la mayoría de los casos. Consulte también "grep" para una matriz compuesta de los elementos de la lista original para los que BLOCK o EXPR evalúan como verdaderos.

Se podría volver a escribir el código de esta manera, que al menos le ahorrará de la adición de una línea adicional:

my @strings = ('abc.abc#a', 'def.g.h#i'); 
foreach my $str (@strings){ 
    (my $copy = $str) =~ s/[^0-9A-Za-z]/_/g; 
    print $copy, "\n"; 
} 
2

Usted está correcto. Como dice Programming Perl, la variable de bucle es un alias para el elemento de matriz actual y debe guardarse la variable de bucle si las modificaciones no afectan al valor original.

3

Siempre se puede hacer una copia de la matriz antes de modificar los elementos en ella (' my 'agregado para apaciguar strict), viz.:

my @strings = ('abc.abc#a', 'def.g.h#i'); 
foreach my $str (my @temp = @strings) { 
    $str =~ s/[^0-9A-Za-z]/_/g; 
    print "$str\n"; #Actually I use the string to manipulate files. 
} 
use Data::Dumper; 
print Dumper(\@strings); 

que devuelve:

abc_abc_a 
def_g_h_i 
$VAR1 = [ 
      'abc.abc#a', 
      'def.g.h#i' 
     ]; 

@temp no tiene alcance fuera del bucle foreach.

1
my @strings = ('abc.abc#a', 'def.g.h#i'); 

print join("\n", (map { $_ =~ s/[^0-9A-Za-z]/_/gr } @strings))."\n"; 

El r sólo funciona en Perl 5.14 y hacia arriba.

Cuestiones relacionadas