Порядок выполнения с запятой в Perl - PullRequest
4 голосов
/ 09 апреля 2011

Простите за плохую читабельность моих примеров, но этот код предназначен для игры в код, а не для производственного кода.

Рассмотрим следующий скрипт:

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 ]

4 голосов
/ 09 апреля 2011

Аргументы передаются по ссылке в 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 голос
/ 10 апреля 2011

Вот моя попытка разобраться в ваших двух примерах.Рассмотрим следующий сценарий:

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().

В версии 2Perl может просто передать первый аргумент напрямую по ссылке: нет необходимости оценивать большее выражение.Второй аргумент такой же, как в версии 1. В результате dd() получает одну и ту же переменную дважды, как показано в выводе Data::Dumper.

0 голосов
/ 09 апреля 2011

Оригинальный ответ

Вам нужно выполнить это через perl -MO=Deparse,-p.Первый бит кода показывает это:

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

Но второй бит кода показывает это:

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

Confusticated и Bebothered

Видимо этого оказалось недостаточно для того, чтобы в достаточной степени объяснить вопросы некоторым людям.Это не должно было быть, поскольку я думал, что это совершенно ясно.

Принятое решение ошибочно утверждает, что это как-то связано с тем, что 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

Резюме

Теперь я с трудом продемонстрировал, что то, что на самом деле здесь происходит, не имеет ничего общего с передачей по ссылке.Это связано только с порядком оценки, и ничего больше.

...