2013-05-07 99 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变量的。