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ました。これは、実行の順序が異なることを示唆しているように見えますが、その理由はわかりません。

これらの2つの例の動作が異なる原因となる実行順序に関する正確なルールは何ですか?実行の順序は明確に定義されていますか?

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

これがあなたの2つの例を理解するための私の試みです。このスクリプトについて考えてみましょう。

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)が最初の引数として渡されます。2番目の引数も評価されます$z。1に設定され、割り当ての戻り値は$z、であり、$z参照によって。に渡されますdd()

バージョン2では、Perlは最初の引数を参照によって直接渡すことができます。より大きな式を評価する必要はありません。2番目の引数は、バージョン1と同じです。結果は、出力dd()に示されているように、同じ変数を2回受け取ることになります。Data::Dumper

于 2011-04-10T01:16:53.853 に答える
-2

元の回答

これを実行する必要がありますperl -MO=Deparse,-p。コードの最初のビットはこれを示しています:

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

しかし、コードの2番目のビットはこれを示しています。

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";
}

これらの3つはすべて、同じ出力を生成します1+2+3

2番目のバージョン

ここで、元の文字からやり直します。

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";
}

3つのバージョンはすべて、私が本当に望んでいる理由で、同じ結果を印刷します+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 に答える