2013-05-07 28 views
11

Tôi thấy mã này trong câu trả lời cho một bài đăng khác: Why would I use Perl anonymous subroutines instead of a named one?, nhưng không thể hiểu chính xác những gì đang xảy ra, vì vậy tôi muốn tự chạy nó.Các biến được chia sẻ trong ngữ cảnh của các chương trình con với các chương trình con ẩn danh

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! 

Trong ví dụ trên, tôi nhận được một cảnh báo: "Biến $ a sẽ không ở lại chia sẻ tại dòng 15. Rõ ràng, đây là lý do tại sao sản lượng là 'bất ngờ', nhưng tôi vẫn không thực sự hiểu những gì đang xảy ra ở đây.

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 

trong bối cảnh đó, tôi không hiểu những gì đang xảy ra trong ví dụ này một trong hai. Có thể ai đó xin giải thích?

Cảm ơn trước.

Trả lời

13

Nó phải làm với phân tích cú pháp thời gian chạy so với thời gian chạy của các chương trình con. Như thông báo diagnostics nói,

Khi chương trình con bên trong được gọi, nó sẽ thấy giá trị của biến các chương trình con bên ngoài của nó như là trước và trong đầu tiên cuộc gọi đến chương trình con bên ngoài; trong trường hợp này, sau khi cuộc gọi đầu tiên đến chương trình con bên ngoài hoàn tất, các chương trình con bên trong và bên ngoài sẽ không còn chia sẻ giá trị chung cho biến đó nữa. Nói cách khác, biến số sẽ không còn được chia sẻ nữa.

chú thích mã của bạn:

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

sản lượng tiêu biểu:

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

“Phân tích” có lẽ là từ sai ở đây, nhưng “biên soạn” dường như hơi sai quá: IIRC, cho đóng cửa các mã được biên dịch chỉ được kết hợp với một môi trường/phạm vi mới, dẫn đến một CV mới, trong khi các tên miền được đặt tên không bao giờ phục hồi lại một phạm vi mới (không có định nghĩa lại). – amon

+0

Cảm ơn rất nhiều, điều đó rất hữu ích! –

1

Bạn có thể in \ &inner; trong ví dụ đầu tiên (sau khi định nghĩa) và in $ bên trong; trong giây lát.

Những gì bạn thấy là tham chiếu mã hex là bằng trong ví dụ đầu tiên và khác nhau trong giây. Vì vậy, trong ví dụ đầu tiên bên trong được tạo ra chỉ một lần, và nó luôn luôn đóng cửa để $ một biến từ vựng từ cuộc gọi đầu tiên của bên ngoài().

Các vấn đề liên quan