我正在用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...
}
但有更有效的方法来实现这一目标吗?
非常感谢!
答案 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
及更高版本。