1

reduce サブルーチンの List::Util で次のコードを見つけました。

my $caller = caller;
local(*{$caller."::a"}) = \my $a;
local(*{$caller."::b"}) = \my $b;

reduce 関数が次のように呼び出されることは理解できました。

my $sum = reduce { $a + $b } 1 .. 1000;

したがって、コードがサブルーチンで言及されている $a を参照しようとしていることがわかりました。しかし、意図が正しく理解できません。

参考までに、サブルーチンの完全なコードを追加しています

sub reduce (&@) {
  my $code = shift;
  require Scalar::Util;
  my $type = Scalar::Util::reftype($code);
  unless($type and $type eq 'CODE') {
    require Carp;
    Carp::croak("Not a subroutine reference");
  }
  no strict 'refs';

  return shift unless @_ > 1;

  use vars qw($a $b);

  my $caller = caller;
  local(*{$caller."::a"}) = \my $a;
  local(*{$caller."::b"}) = \my $b;

  $a = shift;
  foreach (@_) {
    $b = $_;
    $a = &{$code}();
  }

  $a;
}
4

2 に答える 2

4

$foo次の例では、パッケージ variableを variable にエイリアスしています$bar

*foo = \$bar;

どちらの名前も同じスカラーを参照するため、一方を変更すると他方も変更されます。

$ perl -E'
   *foo = \$bar;
   $bar=123; say $foo;
   $foo=456; say $bar;
   say \$foo == \$bar ? 1 : 0;
'
123
456
1

もちろん、*fooシンボル テーブル エントリなので、完全に修飾できます。次の例では、パッケージ変数$main::fooを にエイリアスしています$bar

*main::foo = \$bar;

または、コンパイル時に名前がわからない場合

my $caller = 'main';
*{$caller."::foo"} = \$bar;   # Symbolic reference

$barもちろん、パッケージ変数と同じくらい簡単にレキシカル変数にすることができます。my $bar;実際にはbegin宣言された変数を返すので、

my $bar;
*foo = \$bar;

次のように書くことができます

*foo = \my $bar;

そう、

my $caller = caller;
local(*{$caller."::a"}) = \my $a;
local(*{$caller."::b"}) = \my $b;

レキシカル変数$a$b同様の名前のパッケージ変数を呼び出し元の名前空間で宣言し、別名を付けます。

localサブが終了すると、すべてが元の状態に戻るだけです。

于 2012-12-10T07:03:13.963 に答える
1

範囲内

Perl には、グローバルとレキシカルという 2 つの変数名スコープ メカニズムがあります。レキシカル var の宣言は で行われmy、閉じ中括弧に遭遇するまで、この名前でアクセス可能です。

一方、グローバル変数はどこからでもアクセスでき、スコープはありません。これらは and で宣言できますがour、が有効でないuse vars場合は宣言する必要strictはありません。ただし、名前空間、またはpackages. 名前空間は、変数名から 2 つのコロン (または単一引用符、ただし絶対に使用しないでください) で区切られた接頭辞です。変数のパッケージ内では、プレフィックスの有無にかかわらず、変数にアクセスできます。パッケージの外では、プレフィックスが必要です。

このlocal関数はやや特殊で、グローバル変数に一時的な値を与えます。この値のスコープは、レキシカル変数のスコープに、このスコープ内で呼び出されるすべてのサブルーチンのスコープを加えたものと同じです。このスコープを終了すると、古い値が復元されます。これを動的スコープと呼びます。

グロブ上

Perl は、名前空間とすべての変数名 ( stashと呼ばれることもあります) を表す大きなハッシュでグローバル変数を編成します。このハッシュの各スロットには、いわゆるグロブがあります。型グロブは、スカラー、配列、ハッシュ、IO、フォーマット、コードなど、Perls の各ネイティブ型のフィールドを持つ特別なハッシュです。追加したい値の参照をグロブに渡すことで、スロットに割り当てます -グロブはそれ自体で適切なスロットを見つけ出します。これは、同じ名前の変数を複数持つことができる理由でもあります ( $thing@thing%thingなどthing())。型グロブには特別な記号、つまりアスタリスクがあります*

の上no strict 'refs'

no strict 'refs'あなたが何をしているのかを知っていれば、それは素晴らしいことです。通常、通常の参照のみを逆参照できます。

my @array = (1 .. 5);
my $arrayref = \@array; # is a reference
push @{$arrayref}, 6; # works
push @{array}, 6; # works; barewords are considered o.k.
push @{"array"}, 6; # dies horribly, if strict refs enabled.

最後の行は文字列を逆参照しようとしましたが、これは悪い習慣と見なされます。ただし、 では、no strict 'refs'ここで行っているように、コンパイル時に名前がわからない変数にアクセスできます。

結論

関数は、呼び出しコードのパッケージの名前を返します。callerつまり、1 つの呼び出しスタック フレームを検索します。この名前は、呼び出し元のパッケージの完全な名前$a$b変数を構築するためにここで使用されるため、接頭辞なしで使用できます。次に、これらの名前はlocal、新しく宣言されたレキシカル変数の参照に割り当てられます。

グローバル変数$a$bは、各パッケージで事前に宣言されています。

ループではforeach、これらのレキシカルには異なる値が割り当てられます (レキシカル変数はグローバル変数よりも優先されます) が、参照のためにグローバル変数$foo::a$foo::$b同じデータを指すため、呼び出しの匿名コールバック サブルーチンがreduce2 つの引数を簡単に読み取ることができます。(これについての詳細は、ikegamisの回答を参照してください。)

(a) 効果が外部から見えず、(b) コールバックが面倒な引数のアンパックを行う必要がないため、この面倒なことはすべて良いことです。

于 2012-12-10T07:16:55.823 に答える