2013-05-07 12 views
11

Widziałem ten fragment kodu w odpowiedzi na inny wpis: Why would I use Perl anonymous subroutines instead of a named one?, ale nie mogłem dokładnie określić, co się dzieje, więc chciałem go uruchomić samodzielnie.Współdzielone zmienne w kontekście podprogramów a anonimowe podprogramy

sub outer 
{ 
    my $a = 123; 

    sub inner 
    { 
    print $a, "\n"; #line 15 (for your reference, all other comments are the OP's) 
    } 

    # At this point, $a is 123, so this call should always print 123, right? 
    inner(); 

    $a = 456; 
} 

outer(); # prints 123 
outer(); # prints 456! Surprise! 

W powyższym przykładzie, otrzymał ostrzeżenie: „Zmienna $ a nie pozostanie na wspólnej linii 15. Oczywiście, jest to, dlaczego wyjście jest«nieoczekiwane», ale nadal nie bardzo rozumiem co tu się dzieje.

sub outer2 
{ 
    my $a = 123; 

    my $inner = sub 
    { 
    print $a, "\n"; 
    }; 

    # At this point, $a is 123, and since the anonymous subrotine 
    # whose reference is stored in $inner closes over $a in the 
    # "expected" way... 
    $inner->(); 

    $a = 456; 
} 

# ...we see the "expected" results 
outer2(); # prints 123 
outer2(); # prints 123 

w tym samym duchu, nie rozumiem, co się dzieje w tym przykładzie albo. Może ktoś proszę wyjaśnić?

góry dziękuję.

Odpowiedz

13

Ma to związek z analizą podprogramów podczas kompilacji w czasie wykonywania. Jak mówi komunikat diagnostics,

Gdy wywoływana jest procedura wewnętrzna, to będzie widać wartość zmienna zewnętrzna podprogram jako to było przed i podczas pierwszej wywołania podprogramu zewnętrznego; w tym przypadku, po pierwszym wywołaniu podprogramu zewnętrznego podprogram wewnętrzny i zewnętrzny nie będą już miały wspólnej wartości dla zmiennej. Innymi słowy zmienna nie będzie już udostępniana.

adnotacji swój kod:

sub outer 
{ 
    # 'my' will reallocate memory for the scalar variable $a 
    # every time the 'outer' function is called. That is, the address of 
    # '$a' will be different in the second call to 'outer' than the first call. 

    my $a = 123; 


    # the construction 'sub NAME BLOCK' defines a subroutine once, 
    # at compile-time. 

    sub inner1 
    { 

    # since this subroutine is only getting compiled once, the '$a' below 
    # refers to the '$a' that is allocated the first time 'outer' is called 

    print "inner1: ",$a, "\t", \$a, "\n"; 
    } 

    # the construction sub BLOCK defines an anonymous subroutine, at run time 
    # '$inner2' is redefined in every call to 'outer' 

    my $inner2 = sub { 

    # this '$a' now refers to '$a' from the current call to outer 

    print "inner2: ", $a, "\t", \$a, "\n"; 
    }; 

    # At this point, $a is 123, so this call should always print 123, right? 
    inner1(); 
    $inner2->(); 

    # if this is the first call to 'outer', the definition of 'inner1' still 
    # holds a reference to this instance of the variable '$a', and this 
    # variable's memory will not be freed when the subroutine ends. 

    $a = 456; 
} 
outer(); 
outer(); 

Typowe wyjście:

inner1: 123  SCALAR(0x80071f50) 
inner2: 123  SCALAR(0x80071f50) 
inner1: 456  SCALAR(0x80071f50) 
inner2: 123  SCALAR(0x8002bcc8) 
+1

„Przetwarzanie” to chyba niewłaściwe słowo tutaj, ale „kompilacja” wydaje się nieco źle też: IIRC, dla zamknięć z skompilowany kod jest po prostu łączony z nowym środowiskiem/zasięgiem, co daje nowe CV, podczas gdy nazwane podsystemy nigdy nie są przywracane do nowego zakresu (bez ponownego definiowania). – amon

+0

Wielkie dzięki, to było bardzo pomocne! –

1

Możesz wydrukować \ &inner; w pierwszym przykładzie (po definicji) i wydrukować $ wewnętrzny; w sekundę.

Co widzisz są sześciokątne referencje kod będące równe w pierwszym przykładzie i różnią się w sekundę. Tak więc w pierwszym przykładzie wewnętrzna jest tworzona tylko raz i zawsze jest zamykana na $ leksykalną zmienną od pierwszego wywołania zewnętrznego().

Powiązane problemy