4

考虑以下脚本:

print'+'x$z,($z=1,$w)?'':$_ for 1..3;

正如我所料,这将打印出来1+2+3。该变量$z最初是未分配的,因此'+'x$z计算为空;之后,$z设置为 1,因此'+'x$z现在计算为+.

但是,如果我更改它以使其$z包含+自身:

print$z,($z='+',$w)?'':$_ for 1..3;

脚本现在打印+1+2+3。这似乎向我暗示执行顺序不同,但我不明白为什么。

关于导致这两个示例行为不同的执行顺序的精确规则是什么?执行顺序是否明确?

4

3 回答 3

4

在 Perl 中,参数是通过引用传递的。

print $z, ($z='+',$w) ? '' : $_;

基本上是

{
   local @_;
   alias $_[0] = $z;
   alias $_[1] = ($z='+',$w) ? '' : $_;
   &print;
}

因为$_[0]别名为$z,所以对 的更改$z会反映在 中$_[0],即使这些更改发生在对参数求值之后。

您可以在下面看到相同的效果:

my $x = 3;
sub f { 
   ++$x;
   print("$_[0]\n");
}
f($x);  # 4
于 2011-04-09T00:14:45.807 回答
1

这是我试图理解你的两个例子的尝试。考虑这个脚本:

use strict;
use warnings;
use Data::Dumper;

sub dd { print Dumper(\@_) }

my $z = 0;

dd($z + 2, ($z = 1));  # Similar to your Version 1.
dd($z,     ($z = 1));  # Similar to your Version 2.

输出,带有一些评论:

$VAR1 = [
          2,              # The constant 2.
          1               # $z by reference, which prints as 1.
        ];
$VAR1 = [
          1,              # $z by reference.
          ${\$VAR1->[0]}  # Ditto.
        ];

在版本 1 中,Perl 不能$z + 2直接传递给dd(). 它必须评估表达式。该评估的结果(常量 2)作为第一个参数传递。第二个参数也被评估:$z设置为 1,赋值的返回值为$z,然后$z通过引用传递给dd()

在版本 2 中,Perl 可以简单地通过引用直接传递第一个参数:无需计算更大的表达式。第二个参数与版本 1 中的相同。结果是dd()两次接收相同的变量,如Data::Dumper输出所示。

于 2011-04-10T01:16:53.853 回答
-2

原始答案

您需要通过perl -MO=Deparse,-p. 第一段代码显示了这一点:

print(('+' x $z), ((($z = 1), $w) ? '' : $_)) foreach (1 .. 3);

但是第二段代码显示了这一点:

print($z, ((($z = '+'), $w) ? '' : $_)) foreach (1 .. 3);

困惑和困扰

显然,这不足以向某些人充分解释问题。它不应该是,因为我认为它非常清楚。

公认的解决方案错误地指出,这在某种程度上与 Perl 通过隐式引用传递标量变量这一事实有关。它与那一点关系都没有。这是一个简单的优先级和评估顺序问题。我原本打算让 Deparse 输出清楚地说明这一点。

显然有些人仍然感到困惑。


第一个版本

很好,这是您的解释,所有这些都在银盘上为您精心准备。

这个:

print'+'x$z,($z=1,$w)?'':$_ for 1..3;

是等价的,由 Deparse 和一些额外的格式提供,到这个:

{
    ($w, $z) = (undef, undef);
    for (1..3) {
        print(("+" x $z), ((($z = 1), $w) ? "" : $_))
    }
} continue {
    print "\n";
}

现在,展开循环并分离出产生以下内容时发生的情况:

{
     ($w, $z) = (undef, undef);
    {
        local $_ = 1;
        $temp = "+" x $z; # $z is undef
        $z = 1;
        print $temp, $_;
    }
    {
        local $_ = 2;
        $temp = "+" x $z; # $z is 1
        $z = 1;
        $_ = $_;
        print $temp, $_;
    }
    {
        local $_ = 3;
        $temp = "+" x $z; # $z is 1
        $z = 1;
        $_ = $_;
        print $temp, $_;
    }
} continue {
    print "\n";
}

所有这三个产生相同的输出:1+2+3.

第二版

现在我们从原来的重新开始:

print$z,($z='+',$w)?'':$_ for 1..3;

并产生一个 deparsing 版本:

{
    ($w, $z) = (undef, undef);
    for (1..3) {
        print($z, ((($z = "+"), $w) ? "" : $_));
    }
} continue {
    print "\n";
}

后跟一个循环展开版本:

{
    ($w, $z) = (undef, undef);
    {
        local $_ = 1;
        $z = "+";
        print $z, $_;
    }
    {
        local $_ = 2;
        $z = "+";
        print $z, $_;
    }
    {
        local $_ = 3;
        $z = "+";
        print $z, $_;
    }
} continue {
    print "\n";
}

所有三个版本,出于我真的希望现在非常清晰的原因,打印相同的结果:+1+2+3


进一步启蒙

追踪正在发生的事情的最佳方法是对其进行跟踪:

tie $z, "Tie::Trace", "z";
tie $w, "Tie::Trace", "w";

($w, $z) = (undef, undef);
print'+'x$z,($z=1,$w)?'':$_ for 1..3;
print "\n";

{
    ($w, $z) = (undef, undef);
    for (1..3) {
        print(("+" x $z), ((($z = 1), $w) ? "" : $_))
    }
} continue {
    print "\n";
}

{
     ($w, $z) = (undef, undef);
    {
        local $_ = 1;
        $temp = "+" x $z; # $z is undef
        $z = 1;
        print $temp, $_;
    }
    {
        local $_ = 2;
        $temp = "+" x $z; # $z is 1
        $z = 1;
        $_ = $_;
        print $temp, $_;
    }
    {
        local $_ = 3;
        $temp = "+" x $z; # $z is 1
        $z = 1;
        $_ = $_;
        print $temp, $_;
    }
} continue {
    print "\n";
}

($w, $z) = (undef, undef);
print$z,($z='+',$w)?'':$_ for 1..3;
print "\n";

{
    ($w, $z) = (undef, undef);
    for (1..3) {
        print($z, ((($z = "+"), $w) ? "" : $_));
    }
} continue {
    print "\n";
}

{
    ($w, $z) = (undef, undef);
    {
        local $_ = 1;
        $z = "+";
        print $z, $_;
    }
    {
        local $_ = 2;
        $z = "+";
        print $z, $_;
    }
    {
        local $_ = 3;
        $z = "+";
        print $z, $_;
    }
} continue {
    print "\n";
}

package Tie::Trace;

sub TIESCALAR {
    my($class, $name, $value) = @_;
    return bless {
        NAME  => $name,
        VALUE => undef,
    } => $class;
}

sub FETCH {
    my($self) = @_;
    my $name = '$' . $self->{NAME};
    my $value = $self->{VALUE};
    print STDERR "[reading value ", defined($value) ? $value : "undef",
            " from $name]\n";
    return $value;
}

sub STORE {
    my($self, $value) = @_;
    my $name = '$' . $self->{NAME};
    print STDERR "[writing value ", defined($value) ? $value : "undef",
            " into $name]\n";
    $self->{VALUE} = $value;
    return $value;
}

当你运行它时,它会产生这个相当令人满意的输出:

[writing value undef into $w]
[writing value undef into $z]
[reading value undef from $z]
[reading value undef from $z]
[writing value 1 into $z]
[reading value undef from $w]
[reading value 1 from $z]
[reading value 1 from $z]
[writing value 1 into $z]
[reading value undef from $w]
[reading value 1 from $z]
[reading value 1 from $z]
[writing value 1 into $z]
[reading value undef from $w]
1+2+3
[writing value undef into $w]
[writing value undef into $z]
[reading value undef from $z]
[reading value undef from $z]
[writing value 1 into $z]
[reading value undef from $w]
[reading value 1 from $z]
[reading value 1 from $z]
[writing value 1 into $z]
[reading value undef from $w]
[reading value 1 from $z]
[reading value 1 from $z]
[writing value 1 into $z]
[reading value undef from $w]
1+2+3
[writing value undef into $w]
[writing value undef into $z]
[reading value undef from $z]
[reading value undef from $z]
[writing value 1 into $z]
[reading value 1 from $z]
[reading value 1 from $z]
[writing value 1 into $z]
[reading value 1 from $z]
[reading value 1 from $z]
[writing value 1 into $z]
1+2+3
[writing value undef into $w]
[writing value undef into $z]
[writing value + into $z]
[reading value undef from $w]
[reading value + from $z]
[writing value + into $z]
[reading value undef from $w]
[reading value + from $z]
[writing value + into $z]
[reading value undef from $w]
[reading value + from $z]
+1+2+3
[writing value undef into $w]
[writing value undef into $z]
[writing value + into $z]
[reading value undef from $w]
[reading value + from $z]
[writing value + into $z]
[reading value undef from $w]
[reading value + from $z]
[writing value + into $z]
[reading value undef from $w]
[reading value + from $z]
+1+2+3
[writing value undef into $w]
[writing value undef into $z]
[writing value + into $z]
[reading value + from $z]
[writing value + into $z]
[reading value + from $z]
[writing value + into $z]
[reading value + from $z]
+1+2+3

概括

我现在已经费力地证明了这里实际发生的事情与传递引用无关。它仅与评估顺序有关,与其他无关。

于 2011-04-09T00:53:56.747 回答