局部变量保留值



所以,我刚刚找到了一个可以在这个琐碎的子程序中演示的错误:

sub foo {
    my $bar = shift or die "Missing bar", # <--- not a semicolon
    my @items = ();
    push @items, $bar;
    return @items;
}

显然,错误在于子程序的第一行以逗号结尾。这产生了一些相当不寻常的后果,可以看出:

say foo(1); # 1
say foo(1); # 11
say foo(1); # 111
say foo(1); # 1111

现在,我知道这不是一个语法错误,因为逗号运算符是如何工作的。我知道@items没有设置为(),因为没有到达or的右侧。我的问题是,在子例程中用my声明的变量如何允许数据在子例程调用之间持久存在?似乎my正在以某种方式转变为our

B::Deparse在这样的练习中是非常宝贵的:

$ perl -MO=Deparse 31191808.pl
sub foo {
    die 'Missing bar', my(@items) = () unless my $bar = shift @_;
    push @items, $bar;
    return @items;
}

这使得这成为CCD_ 8技巧/bug/好奇心的变体。它的作用是创建一个词法但静态的变量,每次调用foo时都不会重新初始化该变量。

您正在做的与这个片段非常相似:

use v5.14; # Implies strict
sub foo {
    my @something= () if 0;
    push @something, shift;
    say @something;
}
foo($_) for 1..5;

输出为:

1
12
123
1234
12345

在Perl中,有条件地声明一个变量使它只在条件为true时分配一个值。如果将if 0更改为if $_[0] == 3,则会得到完全不同的数字序列。这实际上是Perl中的一个旧错误,无法再修复,因为很多代码可能依赖于它,但如果幸运的话,您可能会看到以下警告:"在false conditional中不推荐使用my((">

您已发现comma-operator

来自perldoc perlop:

二进制","是逗号运算符。在标量上下文中,它评估left参数,丢弃该值,然后计算其right参数,并返回该值。

因此,这实际上被认为是一个单一的声明:

my $bar = shift or die "Missing bar", my @items = ();

Perl计算LHS并丢弃结果,因为这是一个并没有真正丢弃任何东西的赋值。1仍然被赋值给$bar,然后计算RHS并返回该值。这里需要注意的一点是,这意味着@items被初始化为子中的静态词法变量,但在对foo()的调用中保持静态。类似于state变量的工作方式。

此时,在子程序中,您已将1分配给$bar。下一行是:

push @items, $bar;

Perl将$bar推送到静态词法变量@items上。下一个语句返回单个元素1的列表。

foo的后续调用继续向@items数组添加元素,然后返回这些元素。这就是为什么您从子程序调用中看到1的数量在增加。

最新更新