2010-03-31 7 views
26

सीखने के प्रयोजनों के लिए, मैं पर्ल में घटना-संचालित कार्यक्रमों के निर्माण के विचार के साथ चारों ओर घूम रहा हूं और ध्यान दिया कि यह अच्छा हो सकता है कि एक ईवेंट हैंडलर के रूप में पंजीकृत सबराउटिन विफल हो सकता है, बस एक और कॉल शेड्यूल करें बाद में खुद के लिए। अब तक, मैं कुछ इस तरह के साथ आने के लिए है:पर्ल में, एक सबराउटिन को कोडफ्रफ़ कैसे प्राप्त हो सकता है जो स्वयं को इंगित करता है?

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

वास्तव में अच्छा सवाल है। –

+4

यदि आप प्रोग्राम के उसी भाग में बहुत कुछ कर रहे हैं, तो परिपत्र संदर्भ के कारण आपके पास मेमोरी रिसाव होगा। आप इससे बचने के लिए स्केलर :: उपयोग :: कमजोर() का उपयोग कर सकते हैं, या नीचे सुझाए गए अनुसार उप :: वर्तमान या वाई-संयोजक का उपयोग कर सकते हैं। चर्चा के लिए http://use.perl.org/~Aristotle/journal/30896 देखें। यदि यह कोड लगातार वातावरण में नहीं है, तो ऊपर दिया गया आपका कोड ठीक है। – runrig

+0

रन्रिग: लिंक के लिए धन्यवाद। अब मेरा सिर चकरा रहा है। :-) शायद मैं वास्तव में कुछ सीखने जा रहा हूं ... – hillu

उत्तर

14

मुझे लगता है कि Sub::Current आपकी समस्या को ठीक करेगा।

+0

धन्यवाद। हां, जाहिर है यह वही करता है जो मैं चाहता हूं। बहुत बुरा यह पर्ल के आंतरिक में ऐसा करने के लिए चारों ओर पोक करने की जरूरत है ... – hillu

5

यदि आप $cb के मान को फिर से नहीं बदलते हैं, तो आप इसका उपयोग कर सकते हैं। यदि नहीं, तो उसे पकड़ने के लिए एक स्केलर को परिभाषित करें और इसे फिर से न बदलें। उदाहरण के लिए:

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

एक अतिरिक्त चर का उपयोग किए बिना वर्तमान सबरूटीन के लिए एक संदर्भ पाने के लिए आपको कार्यात्मक प्रोग्रामिंग से एक उपकरण का उपयोग कर सकते हैं, वाई-Combinator, जो मूल रूप से बंद होने बनाने की प्रक्रिया दूर सार। यहाँ एक perlish संस्करण है:

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

क्या यह सही है? मुझे यकीन नहीं है कि $ करी क्या है। पहले मैंने सोचा कि मेरा जवाब अलग था, लेकिन अब मुझे लगता है कि आपका तीसरा "$ करी" "$ कोड" होना चाहिए और आपको $ करी को कुचलना चाहिए। और यह जवाब मुझे लगता है कि आपको स्मृति रिसाव के साथ एक परिपत्र संदर्भ देगा। – runrig

+0

@runrig => यदि आप केवल मूल कोड को उप में भेजते हैं, तो उस रेफरी का उपयोग करने वाली किसी भी अगली कॉल को कोडफ्रफ़ को उनके पहले तर्क के रूप में प्राप्त नहीं किया जाएगा। मेरे पास कोड में एक परिपत्र संदर्भ था। मैंने समस्या को ठीक करने के लिए इसे अद्यतन किया है। –

+0

हाँ, मुझे चले जाने के लगभग 5 मिनट बाद मिल गया। मैंने अपना उत्तर भी अपडेट किया है :-) – runrig

4

एक निश्चित बिंदु Combinator उपयोग करके आप अपने $ सीबी समारोह समारोह में ही लिख सकते हैं के रूप में यदि पहला तर्क था , यह उपयोगिता प्रदान करते हैं।

+0

'sched_event' पर आपका दूसरा कॉल $ cb की एक प्रति पारित नहीं किया जाएगा जो स्वयं को अपना पहला तर्क प्राप्त करता है। मैंने एक ऐसा संस्करण दिखाने के लिए अपना उत्तर अपडेट कर दिया है जो अब स्मृति को रिसाव नहीं करता है। –

+0

आप सही हैं, उप कॉल से कॉल करने के बजाए एक कॉलबैक फ़ंक्शन किसी अन्य फ़ंक्शन पर पास किया जा रहा है, इसलिए सब को हर बार लपेटने की आवश्यकता होती है। अपडेट किया गया। – runrig

13

__SUB__ 5,16 में जोड़ा गया है:

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); 
संबंधित मुद्दे