2013-05-07 12 views
11

Ho visto questo bit di codice in una risposta a un altro post: Why would I use Perl anonymous subroutines instead of a named one?, ma non riuscivo a capire esattamente cosa succedeva, quindi volevo eseguirlo da solo.Variabili condivise nel contesto di subroutine vs subroutine anonime

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! 

Nell'esempio di cui sopra, ho ricevuto un avvertimento: "variabile $ a non rimarrà condiviso a linea 15. Ovviamente, questo è il motivo per cui l'uscita è 'inaspettato', ma io ancora non capisco cosa sta succedendo qui.

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 

Allo stesso modo, non capisco cosa sta succedendo in questo esempio sia. qualcuno potrebbe spiegare?

Grazie in anticipo.

risposta

13

Ha a che fare con l'analisi in fase di compilazione e in fase di esecuzione delle subroutine. Come dice il messaggio diagnostics,

Quando la subroutine interna si chiama, si vedrà il valore della variabile della subroutine esterna come era prima e durante la prima chiamata alla subroutine esterna; in questo caso, dopo che è stata completata la prima chiamata alla subroutine esterna , le subroutine interne ed esterne non condivideranno più un valore comune per la variabile . In altre parole, la variabile non sarà più condivisa.

Annotare il codice:

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

uscita tipico:

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

“Analisi” è probabilmente la parola sbagliata qui, ma “compilation” sembra troppo leggermente sbagliato: IIRC, per le chiusure delle il codice compilato viene semplicemente combinato con un nuovo ambiente/ambito, risultando in un nuovo CV, mentre i sottotitoli con nome non sono mai in un nuovo ambito (senza una ridefinizione). – amon

+0

Grazie mille, è stato molto utile! –

1

È possibile stampare \ &inner; nel primo esempio (dopo la definizione) e stampare $ inner; nel secondo

Quello che si vede sono i riferimenti codice esadecimale che sono uguali nel primo esempio e si differenziano per secondo. Quindi, nel primo esempio inner viene creato solo una volta e chiude sempre a $ una variabile lessicale dalla prima chiamata di outer().

Problemi correlati