2008-09-21 11 views
11

क्या किसी को पर्ल में आलसी-मूल्यांकन सूचियों के लिए अच्छा समाधान मिला है? टाई-इंग @list से, उदाहरण के लिए - मैं तरीके का एक संख्या की तरहक्या पर्ल 6 के इस तरफ आलसी सूचियों के लिए एक पर्ल समाधान है?

for my $item (map { ... } @list) { 
} 

कुछ चालू करने के लिए एक आलसी मूल्यांकन में की कोशिश की है। मैं इसे तोड़ने और इसे करने के लिए एक स्रोत फ़िल्टर लिखने से बचने की कोशिश कर रहा हूं, क्योंकि वे कोड को डीबग करने की आपकी क्षमता के साथ गड़बड़ करते हैं। क्या किसी को भी कोई सफलता मिली है। या क्या आपको बस थोड़ी देर लूप करना है और थोड़ी देर लूप का उपयोग करना है?

नोट: मुझे लगता है कि मुझे यह उल्लेख करना चाहिए कि मैं कभी-कभी सूचियों को बदलने के लिए कभी-कभी लंबी grep-map श्रृंखलाओं पर झुका हुआ हूं। तो यह फोरैच लूप या थोड़ी देर लूप नहीं है। यह है कि मानचित्र अभिव्यक्ति एक ही लंबवत स्थान में अधिक कार्यक्षमता पैक करने के लिए होते हैं।

उत्तर

13

जैसा कि पहले उल्लेख किया गया है, प्रत्येक के लिए (प्रत्येक) एक उत्सुक पाश है, इसलिए यह शुरू करने से पहले पूरी सूची का मूल्यांकन करना चाहता है।

सादगी के लिए, मैं आलसी मूल्यांकन किए गए सरणी की कोशिश करने के बजाए एक इटरेटर ऑब्जेक्ट या क्लोजर का उपयोग करने की सलाह दूंगा। जबकि आप आलसी मूल्यांकन किए गए अनंत सूची के लिए एक टाई का उपयोग कर सकते हैं, तो आप पूरी सूची (या पूरी सूची का आकार भी) के लिए पूछे जाने पर परेशानियों में भाग सकते हैं यदि आप कभी भी (प्रत्यक्ष या अप्रत्यक्ष रूप से ऊपर के उपरोक्त में) पूछें) ।इसका इस्तेमाल करने के

sub make_iterator { 
    my ($value, $max, $step) = @_; 

    return sub { 
     return if $value > $max; # Return undef when we overflow max. 

     my $current = $value; 
     $value += $step;   # Increment value for next call. 
     return $current;   # Return current iterator value. 
    }; 
} 

और फिर:

एक पूर्ण वर्ग लिख रहे हैं या किसी भी मॉड्यूल का उपयोग कर के बिना, आप बस बंद होने का उपयोग करके एक सरल इटरेटर कारखाना बना सकते हैं

# All the even numbers between 0 - 100. 
my $evens = make_iterator(0, 100, 2); 

while (defined(my $x = $evens->())) { 
    print "$x\n"; 
} 

वहाँ भी Tie::Array::Lazy है सीपीएएन पर मॉड्यूल, जो आलसी सरणी के लिए एक बहुत समृद्ध और पूर्ण इंटरफ़ेस प्रदान करता है। मैंने मॉड्यूल का उपयोग स्वयं नहीं किया है, इसलिए आपका माइलेज भिन्न हो सकता है।

शुभकामनाएँ

,

पॉल

+1

यदि आप इस तरह के प्रोग्रामिंग के बारे में अधिक जानना चाहते हैं, तो मार्क जेसन डोमिनस की पुस्तक "हायर ऑर्डर पर्ल" पढ़ें। बहुत अच्छा, आईएमएचओ। – moritz

+2

/foreach करने के लिए * नहीं * सीमा ऑपरेटर के विशेष मामले में पूरी सूची प्राप्त करें। – user11318

2

यदि मुझे सही याद है, तो/foreach पूरी सूची को पहले भी प्राप्त करें, इसलिए आलसी मूल्यांकन सूची पूरी तरह से पढ़ी जाएगी और फिर यह तत्वों के माध्यम से फिर से शुरू हो जाएगी। इसलिए, मुझे लगता है कि थोड़ी देर के लूप का उपयोग करने का कोई दूसरा तरीका नहीं है। लेकिन हो सकता है मैं गलत हूं।

my $list = sub { return calculate_next_element }; 
while(defined(my $element = &$list)) { 
    ... 
} 

सब के बाद, मुझे लगता है कि एक टाई के रूप में आप पर्ल में प्राप्त कर सकते हैं के रूप में करीब है:

थोड़ी देर के पाश का लाभ यह है कि आप कर सकते हैं नकली एक कोड संदर्भ के साथ एक lazily का मूल्यांकन सूची की अनुभूति होती है 5.

+0

क्यों न केवल मेरी $ list = \ & calculate_next_element; ? या कोड संदर्भ छोड़ें और गणनाate_next_element सीधे कॉल करें? – cjm

+0

/foreach करने के लिए * नहीं * सीमा ऑपरेटर के मामले में पूरी सूची प्राप्त करें। अन्यथा वे करते हैं। – user11318

+0

cjm: यह सिर्फ * गणना-आपके-अगले-तत्व-* के लिए प्लेसहोल्डर के रूप में था, वास्तव में फ़ंक्शन कॉल नहीं। अन्यथा आप बिल्कुल सही हैं। – jkramer

3

Use an iterator या सीपीएएन से Tie::LazyList का उपयोग करने पर विचार करें (जो एक टैड दिनांकित है)।

5

कम से कम एक विशेष मामला है जहां संपूर्ण सूची को एक बार में उत्पन्न करने के लिए फॉरवर्ड और फोरच अनुकूलित नहीं किया गया है। और वह रेंज ऑपरेटर है। तो तुम कह का विकल्प है:

for my $i (0..$#list) { 
    my $item = some_function($list[$i]); 
    ... 
} 

और इस सरणी के माध्यम से पुनरावृति जाएगा, हालांकि आप की तरह तब्दील हो, सामने मूल्यों की एक लंबी सूची बनाने के बिना।

आप तत्वों की चर संख्या वापस जाने के लिए अपने नक्शे बयान चाहते हैं, तो आप के बजाय ऐसा कर सकता है:

for my $i (0..$#array) { 
    for my $item (some_function($array[$i])) { 
    ... 
    } 
} 

यदि आप इससे अधिक व्यापक आलस्य चाहते हैं, तो आपका सबसे अच्छा विकल्प कैसे बंद उपयोग करने के लिए सीखने के लिए है आलसी सूचियां उत्पन्न करने के लिए। एमजेडी की उत्कृष्ट पुस्तक उच्च आदेश पर्ल आपको उन तकनीकों के माध्यम से चल सकती है। हालांकि चेतावनी दीजिये कि वे आपके कोड में बहुत बड़े बदलाव शामिल करेंगे।

9

[Sidenote: ध्यान रखें कि एक नक्शा/ग्रेप श्रृंखला के साथ प्रत्येक व्यक्ति के कदम के लिए उत्सुक है रहो। यदि आप इसे एक बार में एक बड़ी सूची देते हैं, तो आपकी समस्याएं अंतिम foreach की तुलना में बहुत जल्दी शुरू होती हैं।]

एक पूर्ण पुनर्लेख से बचने के लिए आप क्या कर सकते हैं अपने लूप को बाहरी पाश से लपेटना है। इस लेखन के बजाय:

for my $item (map { ... } grep { ... } map { ... } @list) { ... } 

... इस तरह इसे लिखने:

while (my $input = calculcate_next_element()) { 
    for my $item (map { ... } grep { ... } map { ... } $input) { ... } 
} 

यह महत्वपूर्ण अपने मौजूदा कोड को फिर से लिखने के लिए होने से बचाता है, और जब तक सूची नहीं उगता के कई आदेशों द्वारा परिवर्तन के दौरान परिमाण, आपको लगभग सभी लाभ मिलते हैं जो पुनरावर्तक शैली की पुनरावृत्ति प्रदान करते हैं।

7

यदि आप आलसी सूचियां बनाना चाहते हैं, तो आपको अपना खुद का इटरेटर लिखना होगा। एक बार आपके पास ऐसा करने के बाद, आप Object::Iterate जैसे कुछ का उपयोग कर सकते हैं जिसमें map और grep के इटरेटर-जागरूक संस्करण हैं। उस मॉड्यूल के स्रोत पर नज़र डालें: यह बहुत आसान है और आप देखेंगे कि अपने स्वयं के इटरेटर-जागरूक सबराउटिन कैसे लिखें।

गुड लक, :)

3

मैं perlmonks.org में एक समान प्रश्न पूछा, और BrowserUk एक बहुत अच्छी ढांचे in his answer दे दी है। असल में, आलसी मूल्यांकन पाने का एक सुविधाजनक तरीका गणना के लिए धागे को जन्म देना है, कम से कम जब तक आप सुनिश्चित हैं कि आप परिणाम चाहते हैं, अभी नहीं। यदि आप आलसी मूल्यांकन चाहते हैं कि विलंबता को कम न करें लेकिन गणना से बचने के लिए, मेरा दृष्टिकोण मदद नहीं करेगा क्योंकि यह एक पुश मॉडल पर निर्भर करता है, न कि पुल मॉडल। संभवतः Coro आउटलाइन का उपयोग करके, आप इस दृष्टिकोण को एक (सिंगल-थ्रेडेड) पुल मॉडल में भी बदल सकते हैं।

इस समस्या पर विचार, मैं भी टाई-इंग धागा परिणाम के लिए एक सरणी पर्ल कार्यक्रम अधिक map की तरह प्रवाहित होगा, लेकिन अब तक, मैं parallel "कीवर्ड" शुरू करने की मेरी एपीआई की तरह बनाने के लिए (एक वस्तु निर्माता जांच की जबकि छिपाने में) और फिर परिणाम पर कॉलिंग विधियों। कोड का अधिक दस्तावेज संस्करण that thread के उत्तर के रूप में पोस्ट किया जाएगा और संभवतः सीपीएएन पर भी जारी किया जाएगा।

4
कि मैं सिर्फ CPAN पर मॉड्यूल List::Gen लिखा उल्लेख करने के लिए मरे हुओं में से यह वापस लाना

जो वास्तव में है क्या पोस्टर लिए देख रहा था:

use List::Gen; 

for my $item (@{gen { ... } \@list}) {...} 

सूचियों के सभी गणना आलसी हैं, और नक्शे देखते हैं/grep समकक्ष कुछ अन्य कार्यों के साथ।

प्रत्येक कार्य एक 'जनरेटर' देता है जो एक बंधे सरणी का संदर्भ है। आप सीधे बंधे सरणी का उपयोग कर सकते हैं, या उपयोगकर्ता विधियों का उपयोग करने के लिए उपयोगकर्ता विधियों का एक गुच्छा हैं।

+0

मैं इसे देख लूंगा। धन्यवाद। – Axeman

+0

कोई समस्या नहीं है, अगर ऐसी कोई विशेषताएं हैं जो आपको लगता है कि इसमें होना चाहिए तो बस मुझे बताएं। –