2010-03-31 24 views
26

Vì mục đích học tập, tôi đang đùa giỡn với ý tưởng xây dựng các chương trình hướng sự kiện trong Perl và nhận thấy rằng có thể tốt hơn nếu một chương trình con đã được đăng ký như một trình xử lý sự kiện có thể bị thất bại, chỉ cần lên lịch một cuộc gọi khác cho chính nó sau này. Cho đến nay, tôi có đưa ra một cái gì đó như thế này:Trong Perl, làm thế nào một chương trình con có thể nhận được một coderef trỏ đến chính nó?

my $cb; 
my $try = 3; 
$cb = sub { 
    my $rc = do_stuff(); 
    if (!$rc && --$try) { 
     schedule_event($cb, 10); # schedule $cb to be called in 10 seconds 
    } else { 
     do_other_stuff; 
    } 
}; 
schedule_event($cb, 0); # schedule initial call to $cb to be performed ASAP 

Có cách nào mà mã bên trong tiểu có thể truy cập vào coderef đó phụ vì vậy tôi có thể làm mà không sử dụng một biến thêm? Tôi muốn lập lịch cuộc gọi ban đầu như thế này.

schedule_event(sub { ... }, 0); 

tôi suy nghĩ đầu tiên của việc sử dụng caller(0)[3], nhưng điều này chỉ mang lại cho tôi một tên chức năng, (nếu không có tên), không phải là một tài liệu tham khảo đang mà có một pad gắn liền với nó.

+1

Câu hỏi thực sự thú vị. –

+4

Nếu bạn làm điều này rất nhiều trong cùng một chương trình, bạn sẽ bị rò rỉ bộ nhớ do tham chiếu vòng tròn. Bạn có thể sử dụng Scalar :: Util :: weaken() để tránh điều này, hoặc sử dụng Sub :: Current hoặc Y-combinator như gợi ý bên dưới. Xem http://use.perl.org/~Aristotle/journal/30896 để thảo luận. Nếu mã này không ở trong môi trường liên tục, mã của bạn ở trên có thể là tốt. – runrig

+0

runrig: Cảm ơn bạn đã liên kết. Đầu của tôi đang quay. :-) Có lẽ tôi thực sự sẽ học một cái gì đó ... – hillu

Trả lời

14

Tôi nghĩ rằng Sub::Current sẽ khắc phục sự cố của bạn.

+0

Cảm ơn. Có, rõ ràng điều này làm những gì tôi muốn. Quá xấu nó cần phải poke xung quanh trong nội bộ của Perl để làm điều đó ... – hillu

5

Nếu bạn không thay đổi lại giá trị của $cb, bạn có thể sử dụng điều đó. Nếu không, hãy xác định một vô hướng để giữ nó và không thay đổi nó một lần nữa. Ví dụ:

my $cb = do { 
    my $sub; 
    $sub = sub { contents using $sub here } 
} 
14

Để có được một tham chiếu đến các chương trình con hiện tại mà không sử dụng một biến thêm, bạn có thể sử dụng một công cụ từ lập trình chức năng, Y Combinator, mà về cơ bản tóm tắt đi những quá trình tạo việc đóng cửa. Dưới đây là một phiên bản Perlish:

use Scalar::Util qw/weaken/; 

sub Y (&) { 
    my ($code, $self, $return) = shift; 
    $return = $self = sub {$code->($self, @_)}; 
    weaken $self; # prevent a circular reference that will leak memory 
    $return; 
} 

schedule_event(Y { my $self = shift; ... }, 0); 
+0

Điều này có đúng không? Tôi không chắc những gì $ cà ri là cho. Lúc đầu, tôi nghĩ rằng câu trả lời của tôi là khác nhau, nhưng bây giờ tôi nghĩ rằng 3 "$ cà ri" của bạn nên được "$ code" và bạn nên bỏ $ cà ri. Và câu trả lời này tôi nghĩ sẽ cung cấp cho bạn một tham chiếu vòng tròn với một rò rỉ bộ nhớ. – runrig

+0

@runrig => nếu bạn chỉ chuyển mã ban đầu vào phần phụ, thì mọi cuộc gọi tiếp theo sử dụng ref đó sẽ không còn nhận được coderef làm đối số đầu tiên của chúng. Có một tham chiếu vòng tròn trong đoạn mã tôi có. Tôi đã cập nhật nó để sửa vấn đề. –

+0

Vâng, tôi nhận được khoảng 5 phút sau khi tôi bỏ đi. Tôi cũng đã cập nhật câu trả lời của mình :-) – runrig

4

Sử dụng một combinator điểm cố định, bạn có thể viết cb chức năng $ của bạn như nếu đối số đầu tiên là chức năng riêng của mình:

sub U { 
    my $f = shift; 
    sub { $f->($f, @_) } 
} 

my $cb = sub { 
    my $cb = shift; 
    ... 
    schedule_event(U($cb), 10); 
    ... 
} 

schedule_event(U($cb), 0); 
+0

cuộc gọi thứ hai của bạn tới 'schedule_event' sẽ không được chuyển một bản sao $ cb nhận chính nó làm đối số đầu tiên của nó. Tôi đã cập nhật câu trả lời của mình để hiển thị phiên bản không bị rò rỉ bộ nhớ nữa. –

+0

Bạn nói đúng, một chức năng gọi lại đang được chuyển tới một chức năng khác thay vì chỉ nhận được gọi từ phụ, vì vậy phụ cần được bao bọc mọi lúc. Đã cập nhật. – runrig

13

__SUB__ đã được thêm vào trong 5.16 , cung cấp khả năng sử dụng này.

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