Порядок выполнения с оператором запятой в Perl
Простите плохую читабельность моих примеров, но этот код предназначен для игры в гольф, а не для производственного кода.
Рассмотрим следующий сценарий:
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
. Это, кажется, предложите мне, чтобы порядок исполнения был другим, но я не понимаю, почему.
3 ответа:
Аргументы передаются по ссылке в 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
Вот моя попытка разобраться в двух ваших примерах. Рассмотрим такой сценарий:
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
.
Оригинальный Ответ
Вам нужно прогнать это через
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
Резюме
Теперь я старательно продемонстрировал, что то, что здесь происходит на самом деле, не имеет никакого отношения к проходящей ссылке. Это имеет отношение только к порядку оценки и ни к чему другому.