所以,我刚刚找到了一个可以在这个琐碎的子程序中演示的错误:
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
的数量在增加。