好的,我有以下代码:
use strict;
my @ar = (1, 2, 3);
foreach my $a (@ar)
{
$a = $a + 1;
}
print join ", ", @ar;
那输出是什么?
2, 3, 4
这是怎么回事?为什么会那样?这种情况会一直发生吗?$a不是局部变量吗?他们当时在想什么呢?
好的,我有以下代码:
use strict;
my @ar = (1, 2, 3);
foreach my $a (@ar)
{
$a = $a + 1;
}
print join ", ", @ar;
那输出是什么?
2, 3, 4
这是怎么回事?为什么会那样?这种情况会一直发生吗?$a不是局部变量吗?他们当时在想什么呢?
Perl有很多看起来有点奇怪的语法,这些语法可以极大地简化常见任务(比如迭代列表并以某种方式修改其内容),但如果您不了解它们可能会让您感到困惑。
$a
被赋予数组中的值的引用 - 这允许您在循环内部修改数组。如果您不想这样做,请不要修改$a
。
请参阅perldoc perlsyn:
如果LIST的任何元素是一个左值,则您可以通过在循环内修改VAR来修改它。相反,如果LIST的任何元素不是一个左值,则任何尝试修改该元素的尝试都将失败。换句话说,foreach循环索引变量是您正在循环遍历的列表中每个项目的隐式别名。
这并不是什么奇怪或者异常的语言特性,虽然我发现有多少人在遇到他们不理解的行为时拒绝查看文档而感到奇怪。
$a
在这里是数组元素的别名。只要不在代码中使用 $a =
,就不会修改数组。 :-)
如果我没记错的话,map
、grep
等函数都有相同的别名行为。
正如其他人所说,这是有文档记录的。
我的理解是,@_
、for
、map
和grep
的别名行为不仅提供了速度和内存优化,还为创意提供了有趣的可能性。其本质上是通过引用传递调用构造块,从而避免了不必要的数据复制,从而节省时间和内存。
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 @_;
}
apply
函数。它类似于map
,但是它复制了主题变量而不是使用引用。如果你讨厌编写以下代码: my @foo = map { my $f = $_; $f =~ s/foo/bar/ } @bar;
你会喜欢 应用
,它可以将其转化为:
my @foo = apply { s/foo/bar/ } @bar;
需要注意的是:如果您将只读值传递到修改其输入值的构造之一中,您将收到“尝试修改只读值”的错误。
perl -e '$_++ for "o"'
这里的重要区别在于,当您在for
循环的初始化部分声明一个my
变量时,它似乎具有本地变量和词法变量的某些属性(对内部知识更多的人能否进行澄清?)
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
}
sub{}
将成为对列表$x
中任何元素的闭包别名。 了解这一点,即使是奇怪的,也可以关闭对别名值的引用,甚至可能是全局的,我认为这在任何其他结构中都不可能。our @global = 1 .. 10;
my @subs;
for my $x (@global) {
push @subs, sub {++$x}
}
$subs[5](); # modifies the @global array
。使用全局变量创建别名:
our $g5; *g5 = $global[5]; $g5++;。将其本地化并使用子程序:
sub doSubOnRef(&$) { local $_; *_ = $_[1]; $_[0]->() } doSubOnRef { $_++ } $global[5];。检查它们是否都指向同一个东西:
my @equality = map { $_ == $global[5] } $global[5], $g5, $g5r, doSubOnRef { $_ } $global[5];`。 - Chris Johnsen$subs [5]
是围绕别名的闭包,这个别名也恰好是(但不必是)全局的。我的观点是,我不知道还有其他方法可以使用核心创建围绕别名的闭包。在您的示例中,无法从doSubOnRef返回闭包,因为类型全局别名/本地变量仅适用于包变量,除非上述情况,否则无法关闭它们。 - Eric Strompackage TiedScalarRef; require Tie::Scalar; our @ISA = qw(Tie::StdScalar); sub FETCH { ${$_[0]->SUPER::FETCH(@_)} } sub STORE { ${$_[0]->SUPER::FETCH($_[0])} = $_[1] }
和 package main; my @subs_; for my $i (0..9) { my $r; tie $r, 'TiedScalarRef', \$global[$i]; push @subs_, sub {++$r} } $subs_[5]();
- Chris Johnsen你的 $a 只是在循环遍历列表时作为每个元素的别名使用。它被用来代替 $_。你可以看出 $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;
}
}
尝试
foreach my $a (@_ = @ar)