Perl的foreach和更改循环变量

时间:2010-07-09 23:45:17

标签: perl foreach

我正在用Perl编写一个脚本,并对Perl的foreach构造有疑问。

看来,如果更改其中一个循环变量,它会在实际数组中更改。事实上是这样,还是我做了完全错误的事情?

我想将abc.abc#a之类的字符串更改为abc_abc_a(非字母数字字符的下划线),但我需要保留数组中的原始值以供日后使用。

我的代码看起来像这样:

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

我可以通过执行以下操作来解决问题:

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

但有更有效的方法来实现这一目标吗?

非常感谢!

4 个答案:

答案 0 :(得分:16)

你不是疯了;这是正常的行为。请参阅Foreach循环下的perldoc perlsyn

  

如果LIST的任何元素是左值,您可以通过修改里面的VAR来修改它   环。相反,如果LIST的任何元素不是左值,则任何修改它的尝试   元素会失败。换句话说,“foreach”循环索引变量是隐式的   列表中您正在循环的每个项目的别名。

其他循环迭代器(如map)具有类似的行为:

map BLOCK LIST  
map EXPR,LIST
     

...
  请注意,$ _是列表值的别名,因此可以用它来修改   LIST的元素。虽然这很有用并且受到支持,但它可以   如果LIST的元素不是,则会导致奇怪的结果   变量。为此目的使用常规的“foreach”循环    大多数情况下更清楚。   有关由这些项组成的数组,另请参阅“grep”   BLOCK或EXPR评估为真的原始列表。

您可以通过这种方式重写代码,这至少可以避免添加额外的行:

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";
}

答案 1 :(得分:3)

您可以在修改数组中的元素之前创建数组的副本(添加“my”以安抚strict),即:

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

返回:

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

@temp的范围不在foreach循环之外。

答案 2 :(得分:2)

你是对的。如Programming Perl所述,循环变量是当前数组元素的别名,如果任何修改不影响原始值,则必须保存循环变量。

答案 3 :(得分:1)

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

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

r仅适用于Perl 5.14及更高版本。