2011-07-05 18 views
8

次のコードPerlのforeachループ

#!/usr/bin/env perl 

use strict; 
use warnings; 

my @foo = (0,1,2,3,4); 

foreach my $i (@foo) { 
    sub printer { 
     my $blah = shift @_; 
     print "$blah-$i\n"; 
    } 

    printer("test"); 
} 

は、私が期待する何をしません。

正確には何が起こっていますか? (私はそれをプリントアウトするために期待 "テスト0 \ NTEST-1 \ NTEST-2 \ NTEST-3 \ NTEST-4 \ n")

答えて

19

問題がsub name {...}構築物がそのようにネストすることができないことですforループ内にあります。 sub name {...}が本当にBEGIN {*name = sub {...}}を意味し、開始ブロックは、すぐに彼らが解析されるように実行されるため、

理由があります。したがって、コンパイル時およびサブルーチンの変数バインディングは、コンパイル時に、forループが実行される前に発生します。

#!/usr/bin/env perl 

use strict; 
use warnings; 

my @foo = (0,1,2,3,4); 

foreach my $i (@foo) { 
    my $printer = sub { 
     my $blah = shift @_; 
     print "$blah-$i\n"; 
    }; 

    $printer->("test"); 
} 

、これらの閉鎖は意志あなたの本当のユースケースではおそらく

test-0 
test-1 
test-2 
test-3 
test-4 

を出力します。

は何がやりたいことは、実行時にその変数をバインドする、匿名のサブルーチンを作成することです後でアクセスできるように配列またはハッシュにロードすることができます。

あなたはまだ閉鎖して識別子を裸の単語を使用することができますが、名前はコンパイル時に表示されていることを確認するために少し余分な作業を実行する必要があります。

BEGIN { 
    for my $color (qw(red blue green)) { 
     no strict 'refs'; 
     *$color = sub {"<font color='$color'>@_</font>"} 
    } 
} 

print "Throw the ", red 'ball'; # "Throw the <font color='red'>ball</font>" 
7

エリック・ストロムの答えが正しいか、そしておそらく何を見たいと思っていましたが、拘束力の詳細には入りません。

字句寿命についての簡単な注意事項:字句には、コンパイル時に作成され、この例が示すように、その範囲が入力される前でも、実際に利用可能ですされています

my $i; 
BEGIN { $i = 42 } 
print $i; 

をその後、彼らはスコープの外に行くとき、彼ら彼らはスコープ内にある次の時間まで利用できなくなる:あなたのコードで

print i(); 
{ 
    my $i; 
    BEGIN { $i = 42 } 
    # in the scope of `my $i`, but doesn't actually 
    # refer to $i, so not a closure over it: 
    sub i { eval '$i' } 
} 
print i(); 

、閉鎖はコンパイル時に初期字句$iにバインドされています。 しかし、foreachループは少し奇妙です。 my $iは実際にはレキシカルを作成しますが、foreachループはそれを使用しません。代わりに、各反復のループオーバーされた値の1つにエイリアスを付け、ループの後に元の状態に復元します。あなたのクロージャーはオリジナル語彙$iを参照する唯一のものです。

わずかな変化は、より複雑さを示しています。ここ

foreach (@foo) { 
    my $i = $_; 
    sub printer { 
     my $blah = shift @_; 
     print "$blah-$i\n"; 
    } 

    printer("test"); 
} 

を、元の$iは、コンパイル時に作成され、クロージャは、それに結合します。ループの最初の反復でループが設定されますが、ループの2回目の反復ではクロージャと関連付けられていない新しい$iが作成されます。

+0

非常に興味深い、ありがとう – Snark