2010-07-09 17 views
15

Sto scrivendo uno script in Perl e ho una domanda sul costrutto di Perl foreach.Perl sta foreach e modifica la variabile di loop

Sembra che se si modifica una delle variabili del ciclo, esso cambia nell'array effettivo. È questo il caso, o ho sbagliato qualcosa ?

voglio cambiare una stringa come abc.abc#a a abc_abc_a (sottolinea per i caratteri alfa numerici), ma devono conservare il valore originale nella matrice per un uso successivo.

Ho codice che sembra qualcosa di simile:

@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. 
} 

ho potuto risolvere il problema nel modo seguente:

@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... 
} 

, ma c'è un modo più efficiente per raggiungere questo obiettivo?

Grazie mille!

risposta

16

Non sei pazzo; questo è un comportamento normale. Vedere perldoc perlsyn sotto Foreach loop:

Se qualsiasi elemento di LISTA è un lvalue, è possibile modificarlo modificando VAR all'interno del ciclo . Viceversa, se qualche elemento di LIST NON è un lvalue, qualsiasi tentativo di modificare quell'elemento avrà esito negativo. In altre parole, la variabile di indice del ciclo "foreach" è un alias implicito per ciascun elemento nell'elenco su cui si sta eseguendo il ciclo.

Altri iteratori ad anello, come map hanno un comportamento simile:

map BLOCK LIST 
map EXPR,LIST 

...
Nota che $ _ è un alias per il valore della lista, quindi può essere utilizzato per modificare il elementi della LISTA. Mentre questo è utile e supportato, può causare risultati bizzarri se gli elementi di LIST non sono variabili . Utilizzare un normale ciclo "foreach" per questo scopo sarebbe più chiaro nella maggior parte dei casi. Vedere anche "grep" per un array composto da quegli elementi dell'elenco originale per cui il BLOCCO o EXPR restituisce true.

Si potrebbe riscrivere il codice in questo modo, che almeno si salva di aggiungere una linea supplementare:

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

Sei corretto. Come gli stati Programming Perl, la variabile di ciclo è un alias per l'elemento di matrice corrente ed è necessario salvare la variabile di ciclo se eventuali modifiche non influiscono sul valore originale.

3

Si può sempre fare una copia della matrice prima di modificare gli elementi in esso (' my 'aggiunto per placare strict, vale a dire.:

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); 

che restituisce:

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

@temp non hai portata al di fuori del ciclo foreach.

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

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

L'r funziona solo in Perl 5.14 e fino.

Problemi correlati