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

在for循环范围内声明的Perl标量在迭代之间保留其值,perl,for-loop,Perl,For Loop,这是我的第一个问题,很抱歉,如果这是愚蠢的,但当我最近在生产代码中遇到它时,我真的很困惑。我将问题归结为两个代码块,我希望这两个代码块做同样的事情,即为每次迭代生成一个随机数: for my $num (0 .. 5) { my $id = int rand 10; print "$id\n"; } 及 第一个实现了我所期望的,但是第二个给出了相同的迭代次数$tmp在这种简化中总是未定义的,因此它只是用来显示行为,如果$tmp产生我期望的结果,则省略=$tmp 如果您能

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

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

第一个实现了我所期望的,但是第二个给出了相同的迭代次数<代码>$tmp在这种简化中总是未定义的,因此它只是用来显示行为,如果$tmp产生我期望的结果,则省略
=$tmp


如果您能深入了解为什么会发生这种情况,我将不胜感激。

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

这种奇怪行为的原因是,您已经声明了
$id
,以及对它的赋值,条件是
$tmp
的真实性,这使得Perl非常适合。这是怎么说的

注意:my、state或our modified with语句修饰符条件或循环构造(例如my$x if…)的行为未定义。my变量的值可能是未定义的、任何先前指定的值,或者可能是任何其他值。不要依赖它。未来版本的perl可能会做一些与您试用的perl版本不同的事情。这里有龙

如果您按如下方式更改代码,您可以自己演示这一点,这很好

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

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

您偶然发现了一个长期以来故意未修复的bug,因为它很有用。线路

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++; }
}

在v5.8.5、v5.10.0和v5.14.2.0上测试,谢谢。我以前从未注意到这张便条,或者至少没有考虑过它的含义。或者
my$id=$tmp$tmp:undf
my$id=$tmp | | unde。但是,由于您不区分不同的假值,因此可以同样轻松地使用
my$id=$tmp“自从Perl5.10以来,这种行为已经被弃用”是非常错误的。从那时起,警告就一直出现,但据记录,它被弃用的时间要长得多。事实上,它被记录为未定义的(现在使用非常危险),这比不推荐的(现在随时停止工作)要糟糕得多。@ikegami:虽然这种行为是官方未定义的,但它是众所周知的(如果不是广泛的话)。警告的添加和措辞含蓄地承认它是一个“特征”,尽管它肯定不是故意的。所以,虽然你在技术上是正确的,但我坚持我的措辞。Perl:向后兼容性非常好,甚至连bug都会有弃用周期-P
{
  my $n = 0;
  sub increment { return $n++; }
}