Perl 中逗号运算符的执行顺序

发布于 2024-10-31 04:14:38 字数 471 浏览 5 评论 0原文

考虑以下脚本:

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。这似乎表明执行顺序不同,但我不明白为什么。

导致这两个示例表现不同的执行顺序的确切规则是什么?执行顺序是否明确定义?

Consider the following script:

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

This prints, as I would expect, 1+2+3. The variable $z is initially unassigned, so '+'x$z evaluates to empty; after that, $z is set to 1, so '+'x$z now evaluates to +.

However, if I change this so that $z contains the + itself:

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

the script now prints +1+2+3. This seems to suggest to me that the order of execution is different, but I don’t understand why.

What are the precise rules regarding order of execution that cause these two examples to behave differently? Is the order of execution even well-defined?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

葬花如无物 2024-11-07 04:14:38

在 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

Arguments are passed by reference in Perl.

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

is basically

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

Because $_[0] is aliased to $z, changes to $z are reflected in $_[0], even if those changes occur after the argument is evaluated.

You can see the same effect in the following:

my $x = 3;
sub f { 
   ++$x;
   print("$_[0]\n");
}
f($x);  # 4
浪漫之都 2024-11-07 04:14:38

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

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 输出所示。

Here's my attempt to make sense of your two examples. Consider this script:

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.

The output, with some comments:

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

In Version 1, Perl cannot pass $z + 2 directly to dd(). It must evaluate the expression. The result of that evaluation (the constant 2) is passed as the first argument. The second argument is also evaluated: $z is set to 1, the return value of the assignment is $z, and then $z is passed by reference to dd().

In Version 2, Perl can simply pass the first argument directly by reference: no need to evaluate a larger expression. The second argument is the same as in Version 1. The result is that dd() receives same variable twice, as shown in the Data::Dumper output.

灯下孤影 2024-11-07 04:14:38

原始答案

您需要通过 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;

并生成一个解析版本:

{
    ($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 = '

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

[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

总结

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

. $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; }

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


总结

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

The Original Answer

You need to run this through perl -MO=Deparse,-p. The first bit of code shows this:

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

But the second bit of code shows this:

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

Confusticated and Bebothered

Apparently that proved insufficient to sufficiently explain matters to some people. It was not supposed to be, for I had thought it perfectly clear.

The accepted solution erroneously states that this somehow has something to do with the fact that Perl passes scalar variables by implicit reference. It has nothing to with that at all. It is a simple matter of precedence and order of evaluation. I had intended that the Deparse output should make that clear.

Apparently some are still confused.


The First Version

Very well, here’s your explanation all daintied up on silver platter for you.

This:

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

is equivalent, courtesy of Deparse and some extra formating, to this:

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

Now, unrolling the loop and separating out what happens when produces this:

{
     ($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";
}

All three of those produce identical output: 1+2+3.

The Second Version

Now we start again with the original:

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

and produce a deparsing version:

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

followed by a loop unroll version:

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

All three versions, for reasons I REALLY HOPE ARE NOW ABUNDANTLY CLEAR print the same result: +1+2+3.


For Further Enlightenment

The best way to track down what is happening when is to put a trace on it:

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 = '

When you run that, it produces this rather gratifying output:

[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

Summary

I have now laboriously demonstrated that what is actually happening here as nothing whatsoever to do with pass-by-reference. It has to do with only the order of evaluation alone, and nothing else.

. $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 = '

When you run that, it produces this rather gratifying output:



Summary

I have now laboriously demonstrated that what is actually happening here as nothing whatsoever to do with pass-by-reference. It has to do with only the order of evaluation alone, and nothing else.

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

When you run that, it produces this rather gratifying output:


Summary

I have now laboriously demonstrated that what is actually happening here as nothing whatsoever to do with pass-by-reference. It has to do with only the order of evaluation alone, and nothing else.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文