为什么Perl foreach变量赋值修改数组中的值?

时间:2010-01-13 20:03:33

标签: perl

好的,我有以下代码:

use strict;
my @ar = (1, 2, 3);
foreach my $a (@ar)
{
  $a = $a + 1;
}

print join ", ", @ar;

和输出?

  

2,3,4

到底是什么?为什么这样做?这总是会发生吗?是$ a不是一个真正的局部变量?他们在想什么?

7 个答案:

答案 0 :(得分:24)

Perl有许多这些几乎奇怪的语法事物,它们大大简化了常见任务(比如迭代列表并以某种方式更改内容),但是如果你不了解它们就会绊倒你。

$a别名为数组中的值 - 这允许您修改循环内的数组。如果您不想这样做,请不要修改$a

答案 1 :(得分:22)

请参阅perldoc perlsyn

  

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

关于 记录的 语言功能,没有任何奇怪的奇怪的,尽管我发现有多少奇怪的语言功能人们在遇到他们不理解的行为时拒绝检查文档。

答案 2 :(得分:9)

在这种情况下,

$a是数组元素的别名。只是在代码中没有$a =,您不会修改数组。 : - )

如果我没记错的话,mapgrep等都具有相同的别名行为。

答案 3 :(得分:4)

正如其他人所说,这是记录在案的。

我的理解是@_formapgrep的别名行为提供了速度和内存优化,并为广告素材提供了有趣的可能性。实质上发生的是构造块的传递引用调用。这样可以避免不必要的数据复制,从而节省时间和内存。

use strict;
use warnings;

use List::MoreUtils qw(apply);

my @array = qw( cat dog horse kanagaroo );

foo(@array);


print join "\n", '', 'foo()', @array;

my @mapped = map { s/oo/ee/g } @array;

print join "\n", '', 'map-array', @array;
print join "\n", '', 'map-mapped', @mapped;

my @applied = apply { s/fee//g } @array;

print join "\n", '', 'apply-array', @array;
print join "\n", '', 'apply-applied', @applied;


sub foo {
   $_ .= 'foo' for @_;
}

请注意使用List::MoreUtils apply功能。它的工作方式与map类似,但会复制主题变量,而不是使用引用。如果你讨厌写代码如下:

 my @foo = map { my $f = $_; $f =~ s/foo/bar/ } @bar;

你会爱apply,它会变成:

 my @foo = apply { s/foo/bar/ } @bar;

要注意的事项:如果将只读值传递给修改其输入值的其中一个构造,则会出现“修改了只读值的错误”错误。

perl -e '$_++ for "o"'

答案 4 :(得分:3)

这里的一个重要区别是,当你在my循环的初始化部分声明一个for变量时,它似乎分享了本地和词汇的一些属性(有更多知识的人)内部关心澄清?)

my @src = 1 .. 10;

for my $x (@src) {
    # $x is an alias to elements of @src
}

for (@src) {
    my $x = $_;
    # $_ is an alias but $x is not an alias
}

这有趣的副作用是,在第一种情况下,for循环中定义的sub{}是围绕列表$x别名的任何元素的闭包。知道这一点,有可能(虽然有点奇怪)围绕一个别名的值,甚至可能是一个全局值,我认为任何其他结构都不可能。

our @global = 1 .. 10;
my @subs;
for my $x (@global) { 
    push @subs, sub {++$x}
}

$subs[5](); # modifies the @global array

答案 5 :(得分:0)

当你遍历它时,你的$ a只是被用作列表中每个元素的别名。它被用来代替$ _。你可以告诉$ a不是局部变量,因为它是在块之外声明的。

更明显的是,如果你认为它是$ _(这就是它)的立场,为什么分配给$ a会改变列表的内容。事实上,如果您定义自己的迭代器,则$ _不存在。

foreach my $a (1..10)
    print $_; # error
}

如果您想知道重点是什么,请考虑以下情况:

my @row = (1..10);
my @col = (1..10);

foreach (@row){
    print $_;
    foreach(@col){
        print $_;
    }
}

在这种情况下,为$ _

提供更友好的名称更具可读性
foreach my $x (@row){
    print $x;
    foreach my $y (@col){
        print $y;
    }
}

答案 6 :(得分:0)

尝试

foreach my $a (@_ = @ar)

现在修改$ a不会修改@ar。 适用于v5.20.2