2

次の 2 つの Perl 変数宣言の違いは何ですか?

my $foo = 'bar' if 0;

my $baz;
$baz = 'qux' if 0;

これらがループの先頭に現れると、その違いは顕著になります。例えば:

use warnings;
use strict;

foreach my $n (0,1){
    my $foo = 'bar' if 0;
    print defined $foo ? "defined\n" : "undefined\n";
    $foo = 'bar';
    print defined $foo ? "defined\n" : "undefined\n";
}

print "==\n";

foreach my $m (0,1){
    my $baz;
    $baz = 'qux' if 0;
    print defined $baz ? "defined\n" : "undefined\n";
    $baz = 'qux';
    print defined $baz ? "defined\n" : "undefined\n";
}

結果は

undefined
defined
defined
defined
==
undefined
defined
undefined
defined

if 0失敗したようで、 にfoo再初期化されることはありませんundef。この場合、そもそもどのように宣言されるのでしょうか?

4

2 に答える 2

12

まず、my $foo = 'bar' if 0;は未定義の動作であると文書化されていることに注意してください。つまり、クラッシュを含むあらゆる操作が許可されています。しかし、とにかく何が起こるかを説明します。


my $x文書化された 3 つの効果があります。

  • コンパイル時にシンボルを宣言します。
  • 実行時に新しい変数を作成します。
  • 実行時に新しい変数を返します。

要するに、Scalar x = new Scalar();式で使用された場合に変数を返す点を除いて、Java の に似ていると思われます。

しかし、実際にそのように機能した場合、次のようにすると 100 個の変数が作成されます。

for (1..100) {
   my $x = rand();
   print "$x\n";
}

myこれは、単独のループ反復ごとに 2 つまたは 3 つのメモリ割り当てを意味します。非常に高価な見通し。代わりに、Perl は 1 つの変数のみを作成し、スコープの最後でそれをクリアします。したがって、実際には、my $x実際には次のことを行います。

  • コンパイル時にシンボルを宣言します。
  • コンパイル時に変数を作成します[1]
  • スコープが終了したときに[2]変数をクリアするディレクティブをスタックに置きます。
  • 実行時に新しい変数を返します。

そのため、作成される変数は 1 つだけです[2]。これは、スコープに入るたびに作成するよりもはるかに CPU 効率が高くなります。

my条件付きで実行した場合、またはまったく実行しなかった場合に何が起こるかを考えてみましょう。そうすることで、スタック上の変数をクリアするディレクティブを配置できなくなり、変数がその値を失うことはありません。明らかに、それは意図されたものでmy ... if ...;はないため、許可されていません。


次のように実装を利用するものもあります。

sub foo {
   my $state if 0;
   $state = 5 if !defined($state);
   print "$state\n";
   ++$state;
}

foo();  # 5
foo();  # 6
foo();  # 7

ただし、そうするには、それを禁止するドキュメントを無視する必要があります。上記は、を使用して安全に達成できます

{
   my $state = 5;
   sub foo {
      print "$state\n";
      ++$state;
   }
}

また

use feature qw( state );  # Or: use 5.010;

sub foo {
   state $state = 5;
   print "$state\n";
   ++$state;
}

ノート:

  1. 「変数」にはいくつかの意味があります。ここでどの定義が正確かはわかりませんが、問題ではありません。

  2. サブルーチン自体以外が変数への参照を保持している場合 (REFCNT>1)、または変数にオブジェクトが含まれている場合、ディレクティブは、既存の変数をクリアする代わりに、(スコープの終了時に) 変数を新しい変数に置き換えます。これにより、以下が正常に機能します。

    my @a;
    for (...) {
        my $x = ...;
        push @a, \$x;
    }
    
于 2013-06-28T04:12:07.597 に答える