2010-07-09 12 views
15

Ich schreibe ein Skript in Perl und habe eine Frage zu Perls foreach Konstrukt.Perls Foreach und Ändern der Looping-Variable

Es scheint, dass wenn Sie eine der Loop-Variablen ändern, es im tatsächlichen Array ändert. Ist das tatsächlich der Fall, oder habe ich etwas getan komplett falsch?

Ich möchte eine Zeichenfolge wie abc.abc#a zu abc_abc_a (Unterstreichungen für nicht alphanumerische Zeichen) ändern, aber ich muss den ursprünglichen Wert im Array für die spätere Verwendung beibehalten.

Ich habe Code, der etwa wie folgt aussieht:

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

ich das Problem, indem Sie folgendermaßen vorgehen lösen könnte:

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

aber ist es eine effizientere Art und Weise dies zu erreichen?

Vielen Dank!

Antwort

16

Sie sind nicht verrückt; Das ist normales Verhalten. Siehe perldoc perlsyn unter foreach-Schleifen:

Wenn ein Element der LISTE ein L-Wert ist, können Sie es ändern, indem VAR innerhalb der Schleife zu ändern. Umgekehrt, wenn ein Element von LIST kein Lvalue ist, wird jeder Versuch, dieses Element zu ändern, fehlschlagen. Mit anderen Worten, die Loop-Indexvariable "foreach" ist ein impliziter Alias ​​für jedes Element in der Liste, die Sie durchlaufen.

Andere Schleife Iteratoren wie map ähnliches Verhalten haben:

map BLOCK LIST 
map EXPR,LIST 

...
Beachten Sie, dass $ _ ein Alias ​​für den Listenwert ist, so kann es verwendet werden die modifizieren Elemente der Liste. Während dies nützlich und unterstützt ist, kann es bizarre Ergebnisse verursachen, wenn die Elemente von LIST nicht Variablen sind. Eine regelmäßige "foreach" -Schleife für diesen Zweck wäre in den meisten Fällen klarer. Siehe auch "grep" für ein Array, das aus den Elementen der ursprünglichen Liste besteht, für die BLOCK oder EXPR als true ausgewertet wird.

Sie Ihren Code auf diese Weise neu schreiben könnte, die zumindest Sie Hinzufügen einer zusätzlichen Linie retten würde:

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

Sie haben Recht. Wie bei Programming Perl states, ist die Schleifenvariable ein Alias ​​für das aktuelle Arrayelement und Sie müssen die Schleifenvariable speichern, wenn Änderungen den ursprünglichen Wert nicht beeinflussen.

3

Sie können jederzeit eine Kopie des Arrays, bevor Elemente darin zu modifizieren (‘my "hinzugefügt, um zu beschwichtigen strict), nämlich.:

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

die zurückgibt:

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

@temp nicht Umfang außerhalb der foreach Schleife hat.

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

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

Die r funktioniert nur in Perl 5.14 und höher.