6

这是我关于 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会产生我期望的结果。

我很感激任何关于为什么会发生这种情况的见解。

4

3 回答 3

11

出现这种奇怪行为的原因是,您已经对 的声明以及$id对它的赋值以 的真实性为条件$tmp,这使得 Perl 失败了。perldoc perlsyn有话要说

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

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

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

    $id = int rand 10 unless $id;
    print "$id\n";
}
于 2013-01-07T14:42:45.940 回答
4

语句修饰符的行为在 a 之后是未定义的my ...(参见perlsyn)。所以不要用那个...

于 2013-01-07T14:40:17.033 回答
2

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

my $id = $tmp if $tmp;

将语句修饰符 (the if) 应用于变量声明 (the 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++; }
}
于 2013-01-07T16:24:55.490 回答