2009-06-18 21 views
7

Entonces, nunca supe esto y quiero aclararlo un poco. Sé que si lo haces

foreach (@list){ 

si cambias $ _ en ese bucle, afectará a los datos reales. Sin embargo, no sabía que si lo hizo

foreach my $var1 (@list){ 

Si ha cambiado $ var1 en el bucle que cambiaría los datos reales. : -/Entonces, ¿hay alguna forma de recorrer @list pero mantener la variable como una copia de solo lectura, o una copia que si se cambia no cambiará el valor en @list?

Respuesta

10

$var tiene un alias para cada elemento.

Ver http://perldoc.perl.org/perlsyn.html#Foreach-Loops

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 en la lista en la que está pasando el bucle.

8

La manera más fácil es sólo para copiarlo:

foreach my $var1 (@list) { 
    my $var1_scratch = $var1; 

o

foreach my $var1 (map $_, @list) { 

Pero si $ var1 es una referencia, $ var1_scratch será una referencia a la misma cosa. Para ser realmente seguro, que tendría que utilizar algo como almacenable :: dclone hacer una copia profunda:

foreach my $var1 (@{ Storable::dclone(\@list) }) { 
} 

(no probado). Entonces debería poder cambiar de manera segura $ var1. Pero podría ser costoso si @list es una gran estructura de datos.

+1

tener cuidado, $ var1 no es una referencia, pero un alias. Vea la diferencia: my $ var2 = \ $ list [0]; imprimir "$ var2 \ n"; muestra SCALAR (0x8171880) – wazoox

+3

@wazoox: quiso decir si $ var1 contenía una referencia, p. Ej. if @list contenía un grupo de referencias HASH. Incluso si rompe el aliasing, todavía tiene otra copia de una referencia a la misma cosa. –

1

No sé cómo forzar a la variable a que sea de valor en lugar de por referencia en la propia instrucción foreach. Puede copiar el valor de $_ sin embargo.

#!/usr/perl/bin 
use warnings; 
use strict; 

use Data::Dumper; 

my @data = (1, 2, 3, 4); 

print "Before:\n", Dumper(\@data), "\n\n\n"; 

foreach (@data) { 
    my $v = $_; 
    $v++; 
} 

print "After:\n", Dumper(\@data), "\n\n\n"; 

__END__ 
+2

Donde dices 'my $ v = shift;' te refieres a 'my $ v = $ _;'. – dave4420

+0

@Dave Hinton corrigió el error. @Sukotto intente imprimir $ v en el cuerpo de su ciclo como lo escribió originalmente para ver su error. –

+0

Sí, quise decir 'my $ v = $ _;' :-( – Sukotto

4

Es un alias, no una referencia. Si desea crear sus propios alias (fuera de para) puede usar Data::Alias.

+1

Umm ... +0. Eso es +1 para la aclaración de alias/referencia y -1 para dar instrucciones sobre hacer lo contrario de lo que el OP solicitó . (Creando un alias en lugar de romper el aliasing) –

+0

@Michael Carman ysth ya había dado la respuesta correcta, solo estaba señalando que es posible crear sus propios alias. –

2

La única diferencia entre estos bucles:

foreach (@array) { ... } 
foreach my $var (@array) { ... } 

es la variable de bucle. El aliasing es una función de foreach, no la variable implícita $_. Tenga en cuenta que este es un alias (otro nombre para la misma cosa) y no una referencia (un puntero a una cosa).

En el caso sencillo (y común), se puede romper el aliasing haciendo una copia:

foreach my $var (@array) { 
    my $copy = $var; 
    # do something that changes $copy 
} 

Esto funciona para valores escalares ordinarios.Para referencias (u objetos) necesitaría hacer una copia profunda usando Storable o Clone, que podría ser costoso. Las variables vinculadas también son problemáticas, perlsyn recomienda evitar la situación por completo.

+0

o Clone :: More or Clone :: Fast – ysth

+0

o haga una copia en el foreach con for my $ var (() = @array) – MkV

0

hacer una copia de @List en la sentencia for:

foreach my $var1 (() = @list) { 
    # modify $var without modifying @list here 
}