在for循环范围内声明的Perl标量在迭代之间保留其值

时间:2013-01-07 14:28:14

标签: perl for-loop

这是我关于SO的第一个问题,很抱歉,如果这很愚蠢,但是当我最近在生产代码中遇到它时,这真的让我很困惑。我把我的问题归结为两个代码块,我希望它能做同样的事情,即为每次迭代生成一个随机数:

for my $num (0 .. 5) {
    my $id = int rand 10;
    print "$id\n";    
}

for (0 .. 5) {
    my $tmp;
    my $id = $tmp if $tmp;

    $id = int rand 10 unless $id;
    print "$id\n";
}

第一个执行我期望它做的事情,但第二个执行任意次数迭代的相同数字。 $tmp在此简化中始终未定义,因此仅显示行为,因为省略= $tmp if $tmp会产生我期望的结果。

我很欣赏为何会发生这种情况。

3 个答案:

答案 0 :(得分:11)

出现这种奇怪行为的原因是你已经$id声明以及对它的赋值,条件是$tmp的真相,让Perl变得合适。 perldoc perlsyn对此有所说明

  

注意:my,state或我们使用语句修饰符条件或循环结构修改的行为(例如,我的$ x if ...)是未定义的。 my变量的值可以是undef,任何先前分配的值,或者可能是其他任何值。不要依赖它。 perl的未来版本可能会与您尝试使用的perl版本有所不同。这里是龙。

如果您按如下方式更改代码,则可以自行演示,这样可以正常工作。

for (0 .. 5) {
    my $tmp;
    my $id;
    $id = $tmp if $tmp;

    $id = int rand 10 unless $id;
    print "$id\n";
}

答案 1 :(得分:4)

my ...之后未定义语句修饰符的行为(请参阅perlsyn)。所以不要使用它......

答案 2 :(得分:2)

你偶然发现了一个长期以来一直故意不修复的错误,因为它很有用。这条线

my $id = $tmp if $tmp;

将语句修饰符(if)应用于变量声明(my)。有条件地定义一个变量没有多大意义,但是因为my同时具有编译时和运行时行为,所以这有效地创建了一个状态变量:即一个在词法上限定为封闭块但保持其变量的变量该块执行之间的值。

(故意)调用您所看到的行为的通常形式是

my $x if 0;

自Perl 5.10以来,这种行为已被弃用,它添加了state个变量来干净利落地执行此操作。现代版本的Perl(5.10 +)将发出警告

Deprecated use of my() in false conditional

即使在版本5.10之前使用它也有点差,因为通过添加一个封闭块并在那里声明变量,可以干净地(尽管不是简洁地)模拟状态变量。例如:

{
  my $n = 0;
  sub increment { return $n++; }
}