2011-01-12 19 views
17

Jedną z pierwszych rzeczy, których próbuję się nauczyć w obcym języku programowania, jest to, jak radzi sobie z zamknięciami. Ich semantyka często przeplata się z tym, w jaki sposób język obsługuje zakresy i różne inne podstępne elementy, więc ich zrozumienie ujawnia kilka innych aspektów tego języka. Dodatkowo, zamknięcia są naprawdę mocną konstrukcją i często ograniczają ilość płytek, które muszę wpisać. Więc ja bawić z zamknięciami Perl i natknąłem się na małą Gotcha:

my @closures; 
foreach (1..3) { 
    # create some closures 
    push @closures, sub { say "I will remember $_"; }; 
} 
foreach (@closures) { 
    # call the closures to see what they remember 
    # the result is not obvious 
    &{$_}(); 
} 

Kiedy pisałem powyższy kod Spodziewałem się zobaczyć

I will remember 1 
I will remember 2 
I will remember 3 

lecz mam I will remember CODE(0x986c1f0).

Powyższy eksperyment ujawnił, że $_ jest bardzo zależny od kontekstu i jeśli pojawia się w zamknięciu, to jego wartość nie jest ustalona w momencie tworzenia zamknięcia. Zachowuje się bardziej jak odniesienie. Jakie inne pomysły powinienem mieć świadomość przy tworzeniu zamknięć w perlu?

Odpowiedz

25

Zamknięcia tylko w pobliżu zmiennych leksykalnych; $_ jest zwykle zmienną globalną. W 5.10 i wyżej można powiedzieć, że my $_; ma charakter leksykalny w danym zakresie (chociaż w 5.18 zostało to z mocą wsteczną uznane za eksperymentalne i może ulec zmianie, więc lepiej użyć innej nazwy zmiennej).

To daje wyjście z oczekiwaniami:

use strict; 
use warnings; 
use 5.010; 
my @closures; 
foreach my $_ (1..3) { 
    # create some closures 
    push @closures, sub { say "I will remember $_"; }; 
} 
foreach (@closures) { 
    # call the closures to see what they remember 
    # the result is not obvious 
    &{$_}(); 
} 
+2

Ale to prawie to samo, co użycie 'foreach my $ num ...'. – davidk01

+1

Więc jeśli zamykają się tylko na zmiennych leksykalnych, co dzieje się z innymi rodzajami zmiennych? Czy zamknięcie korzysta z wartości dostępnej w witrynie połączenia, tak jak ma to miejsce w przypadku '$ _'? – davidk01

+1

@ davidk01: jak w każdym innym języku programowania, który dopuszcza zmienne globalne, zamknięcia faktycznie zbliżają się do tej zmiennej, tak jak zwykle. Po prostu zapomniałeś, że jest on globalny i dlatego może się zmienić, gdy inny kod ustawia swoją wartość na coś innego (np. Przez anonimowy sub, gdy się go nazywa). – slebetman

3

$ _ jest zmienną globalną i nie powinny być używane w zamknięciu. Przed użyciem przypisz to do zmiennej o ograniczonym zakresie leksykalnym, jak pokazano na stronie bewlow. Spowoduje to oczekiwanie o/p.

#!/usr/bin/perl -w 

use strict; 
my @closures; 


foreach (1..3) { 
    my $var = $_; 
    push @closures, sub { print "I will remember $var"; }; 
} 

foreach (@closures) { 
    $_->(); 
    print "\n"; 
}