2011-07-05 17 views
8

perl foreach döngüsü

#!/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"); 
} 

ne beklenir yapmaz aşağıdaki kod yönetir.

Tam olarak ne oluyor? (Ben yazdırmak için beklenir "Test-0 \ ntest-1 \ ntest-2 \ ntest-3 \ ntest-4 \ n")

cevap

19

sorun sub name {...} yapı böyle iç içe olamaz ki for döngüsünde.

Nedeni, sub name {...}'un gerçekten BEGIN {*name = sub {...}} anlamına gelir ve başlatılan bloklar ayrıştırılır tamamlanmaz başlatılır. Böylece, alt döngünün derlenmesi ve değişken bağlanması, döngü için hiçbir zaman çalışma şansı elde etmeden önce derleme zamanında gerçekleşir.

#!/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"); 
} 

, bu kapaklar olacak gerçek kullanım durumunda Tahminen

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

yazdırır:

Ne yapmak istediğiniz zamanında değişkenlerini bağlayacak olan anonim alt yordam oluşturmaktır Daha sonra erişilebilmeleri için bir diziye veya kareye yüklenebilir.

Hala kapanışları ile tanımlayıcıları bareword kullanabilirsiniz, ancak isimleri derleme zamanında görünür olduğundan emin olmak için biraz ekstra çalışma yapmak gerekir:

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

Eric Strom cevabı doğrudur ve muhtemelen ne görmek istedim, ama bağlamanın detaylarına girmiyor.

sözcük yaşam süresi ile ilgili kısa bir not: lexicals derleme zamanında oluşturulan ve bu örnekte görüldüğü gibi, onların kapsamı girilir önce bile aslında temin edilebilir:

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

Bundan sonra, onlar kapsam dışında gitmek, onlar onlar kapsamındadır dahaki sefere kadar kullanılamaz hale: kodunuzda

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

, kapatma derleme zamanında ilk sözcük $i bağlıdır. Ancak, foreach döngüler biraz garip; my $i aslında bir sözcük oluştururken, foreach döngüsü bunu kullanmaz; bunun yerine, her yinelemeli döngü değerlerinden birine takma ve sonra döngüden sonra özgün durumuna geri yükler. Kapanışınız bu nedenle orijinal sözlüksel $i referans veren tek şeydir.

hafif varyasyon daha fazla karmaşıklık gösterir: Burada

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

    printer("test"); 
} 

, orijinal $i derleme zamanında oluşturulur ve kapama o bağlanır; Döngünün ilk yinelemesi onu ayarlar, ancak döngünün ikinci yinelemesi, kapanma ile ilişkisiz yeni bir $i oluşturur.

+0

çok ilginç, teşekkürler – Snark