2010-03-31 23 views
26

爲了學習的目的,我正忙於在Perl中構建事件驅動程序 ,並注意到如果被註冊爲事件處理程序的 子例程在失敗時可能會很好,而 只是安排另一個調用自己在以後的時間。到目前爲止,我有 想出這樣的事情:在Perl中,子程序如何獲取指向自身的代碼參考?

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 

是有辦法的子裏面的代碼可以訪問CODEREF到 子,所以我可以不使用額外的變量呢?我想 安排這樣的初始呼叫。

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

使用caller(0)[3]我首先想到的,但這只是給了我一個 函數名,(如果沒有名稱),而不是代碼的參考 具有附加給它墊。

+1

真的很酷的問題。 – 2010-03-31 15:48:57

+4

如果在程序的同一運行過程中執行此操作很多,則由於循環引用會導致內存泄漏。您可以使用Scalar :: Util :: weaken()來避免這種情況,或者使用Sub :: Current或Y-combinator,如下所示。參見http://use.perl.org/~Aristotle/journal/30896進行討論。如果此代碼不在持久環境中,則上面的代碼可能沒問題。 – runrig 2010-03-31 16:18:28

+0

runrig:感謝您的鏈接。我的頭正在旋轉。 :-)也許我真的會去學點東西...... – hillu 2010-03-31 20:57:33

回答

14

我認爲Sub::Current將解決您的問題。

+0

謝謝。是的,顯然這是我想要的。太糟糕了,它需要在Perl的內部戳這麼做...... – hillu 2010-04-04 23:09:00

5

如果您不再更改$cb的值,則可以使用該值。如果沒有,定義一個標量來保存它,不要再次改變它。例如:

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

要到當前子程序的參考,而無需使用一個額外的變量,你可以使用來自函數式編程工具,在Y組合子,基本上抽象了創建閉合的過程。這裏是一個Perl化的版本:

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

這是正確的嗎?我不確定咖喱是什麼。起初,我認爲我的回答是不同的,但現在我認爲你的第三個「咖喱」應該是「美元代碼」,你應該拋棄咖喱。我認爲這個答案會給你帶來內存泄漏的循環引用。 – runrig 2010-04-02 15:47:27

+0

@runrig =>如果您只將原始代碼ref傳遞給sub,則使用該ref的後續調用將不再接收coderef作爲其第一個參數。我的代碼中有一個循環引用。我已更新它來糾正問題。 – 2010-04-02 19:37:29

+0

是的,我走了5分鐘左右。我已經更新了我的答案:-) – runrig 2010-04-02 21:07:35

4

使用定點組合子,你可以寫你的$ CB功能如果第一個參數是函數本身:

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

您對'schedule_event'的第二次調用將不會傳遞一個將自己作爲第一個參數的$ cb副本。我已經更新了我的答案,以顯示不再泄漏內存的版本。 – 2010-04-02 19:43:44

+0

你說得對,一個回調函數被傳遞給另一個函數,而不是從sub中調用,所以sub需要每次都被包裝。更新。 – runrig 2010-04-02 21:10:58

13

__SUB__已經在5.16被添加,提供這種可用性。