2013-05-07 70 views
11

我在另一篇文章的答案中看到了這一點代碼:Why would I use Perl anonymous subroutines instead of a named one?,但無法弄清楚到底發生了什麼,所以我想自己運行它。子例程與匿名子例程中的共享變量

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! 

在上面的例子中,我收到了警告:「變量$ a不會停留在15行 顯然共享的,這就是爲什麼輸出是‘意外’,但我還是不太懂這裏發生了什麼。

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 

本着同樣的精神,我不明白是怎麼發生的事情或者在這個例子中,可能有人請解釋一下嗎?

在此先感謝。

回答

13

它與編譯時與運行時解析子例程有關。作爲diagnostics消息稱,

當內子程序被調用時,它將看到的 值外子程序的變量,因爲它是前和第一 呼叫到外子程序期間;在這種情況下,在第一次調用 外部子程序完成之後,內部和外部子程序將不再爲共享該變量的共同值的 更長。換句話說, 變量將不再被共享。

註解代碼:

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

典型輸出:

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

「分析」可能是錯一個字在這裏,但「彙編」似乎稍有不妥太:IIRC,閉包的編譯後的代碼只是合併到一個新的環境/作用域中,導致一個新的CV,而named subs不會重新映射到一個新的作用域(沒有重新定義)。 – amon 2013-05-07 18:05:19

+0

非常感謝,這非常有幫助! – 2013-05-07 18:39:49

1

您可以在第一個示例中打印\ &inner;(定義之後),並打印$ inner;在第二。

你看到的是十六進制代碼引用這是在第一個例子等於和第二不同。 因此,在第一個例子中,inner僅被創建一次,並且它始終是從outer()的第一個調用中關閉到$ lexical變量的。