原谅我的示例可读性差,但这段代码适用于代码高尔夫,而不适用于生产代码。
考虑以下脚本:
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
。这似乎告诉我,执行的顺序是不同的,但我不明白为什么。
有关执行顺序的精确规则是什么导致这两个示例的行为不同?执行的顺序是否定义明确?
答案 0 :(得分: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
答案 1 :(得分: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
输出中所示。
答案 2 :(得分:-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;
并生成一个去除版本:
{
($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
我现在已经费力地证明了这里实际发生的事情与传递参考没有任何关系。它只与评估的顺序有关,而不是别的。