2015-09-29 14 views
5

Lý do tôi muốn sử dụng các thuê bao ẩn danh thay vì các tên miền được đặt tên là vì tôi muốn xác định các subs này bên trong các thành phần Mason (http://www.masonbook.com/book/chapter-2.mhtml#TOC-ANCHOR-7), không hoạt động tốt với các subs có tên.Có an toàn khi đệ quy gọi một tiểu thư ẩn danh từ người khác?

Ví dụ: nếu tôi viết mã theo cách đó:

my ($first, $second); 
$first = sub { 
    my $val = shift; 
    print "val: $val"; 
    $second->($val); 
}; 
$second = sub { 
    my $val = shift; 
    if (0 < $val) { 
     $val = $val - 1; 
     $first->($val); 
    } 
}; 
$first->(10); 

Có bất kỳ dấu hiệu ẩn nào (ví dụ: rò rỉ bộ nhớ, v.v.) trong phương pháp này không?

Như được giải thích bởi @Schwern, bộ nhớ cho những người đăng ký này sẽ không được Perl phát hành, vì có một tham chiếu vòng tròn giữa chúng.

Nhưng cụ thể hơn, việc cấp phát bộ nhớ có tăng trưởng tuyến tính hay không, vì $ val được tăng lên hay không phụ thuộc vào độ sâu ngăn xếp của trình gọi? Bởi vì tôi có thể đặt các subs này trong mason <% once> blocks, và trong trường hợp đó các subs này sẽ được khởi tạo chỉ một lần.

Trả lời

4

Điều duy nhất tôi có thể nghĩ là các chương trình con sẽ không bao giờ bị phân phối, ngay cả khi $first$second không nằm trong phạm vi. Mã số của $first đề cập đến mã số $second, $second đề cập đến $first. Đây là một cấu trúc dữ liệu hình tròn, và sự phân bổ bộ nhớ của Perl không thể giải quyết được điều đó.

$ perl -wlE 'for (1..10_000) { my($first, $second); $first = sub {}; $second = sub {} } say "Done"; sleep 1000' 

$ perl -wlE 'for (1..10_000) { my($first, $second); $first = sub { $second->() }; $second = sub { $first->() } } say "Done"; sleep 1000' 

Quy trình Perl đầu tiên sử dụng 1912K sau vòng lặp, quy trình thứ hai sử dụng 10320K. Việc đầu tiên sẽ không phát triển bất kể có bao nhiêu CV được tạo ra, thứ hai sẽ là.

Để giải quyết vấn đề này, bạn phải phá vỡ vòng tròn bằng cách xác định số $first hoặc $second. Cái thứ ba gọi undef $first bên trong vòng lặp, bộ nhớ của nó không phát triển.

$ perl -wlE 'for (1..100_000) { my($first, $second); $first = sub { $second->() }; $second = sub { $first->() }; undef $first; } say "Done"; sleep 1000' 
+0

Xin cảm ơn các bạn! Nhưng về cơ bản, nếu tôi định nghĩa các subs ẩn danh bên ngoài vòng lặp, dấu vết bộ nhớ của tôi sẽ không tăng lên? – Yakov

+1

Vòng lặp chỉ phóng đại vấn đề; bạn vẫn phải undef $ đầu tiên hoặc $ thứ hai chức năng để tránh cấu trúc tròn. –

+0

@Yakov Vòng lặp chỉ ở đó để minh họa sự rò rỉ. Nó A) cung cấp một ngữ cảnh từ vựng (nghĩa là khối) trong đó các chương trình con sẽ bị phá hủy khi thoát ra và B) phóng đại rò rỉ bộ nhớ để nó hiển thị trong một công cụ thô như 'ps'. – Schwern

2

Sau đây sẽ là tốt:

sub first { 
    my $val = shift; 
    print "val: $val"; 
    second($val); 
} 

sub second { 
    my $val = shift; 
    if (0 < $val) { 
     $val = $val - 1; 
     first($val); 
    } 
} 

first(10); 

Thông báo trước là bạn sẽ cần phải khai báo các tàu ngầm nếu họ có một nguyên mẫu hoặc nếu bạn muốn bỏ qua các dấu ngoặc xung quanh lập luận của họ.

sub first($); 
sub second($); 

sub first($) { 
    my $val = shift; 
    print "val: $val"; 
    second $val; 
} 

sub second($) { 
    my $val = shift; 
    if (0 < $val) { 
     $val = $val - 1; 
     first $val; 
    } 
} 

first 10; 

Phiên bản của bạn, bị rò rỉ bộ nhớ. Phụ đầu tiên nắm bắt một tham chiếu đến phụ thứ hai, trong đó nắm bắt một tham chiếu đến phụ đầu tiên.

$ perl -e' 
    sub DESTROY { print "Destroyed\n" } 

    { 
     my ($first, $second); 
     $first = sub { $second }; 
     $second = sub { $first }; 
     bless($first); 
    } 

    print("Subs should have been destroyed by now\n"); 
' 
Subs should have been destroyed by now 
Destroyed 

Giải pháp phụ thuộc vào lý do bạn quyết định sử dụng trình đăng ký anon ngay từ đầu.

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