2009-09-16 27 views
7

यह सवाल रूबी 1.9.1 में अंकुशकों का उपयोग करने के बारे में नहीं है, बल्कि मैं उत्सुक हूं कि वे कैसे काम करते हैं। यहाँ कुछ कोड है:रूबी 1.9.1 में गणक कैसे काम करते हैं?

class Bunk 
    def initialize 
    @h = [*1..100] 
    end 

    def each 
    if !block_given? 
     enum_for(:each) 
    else 
     0.upto(@h.length) { |i| 
     yield @h[i] 
     } 
    end 
    end 
end 

उपरोक्त कोड में मैं e = Bunk.new.each, और फिर e.next, e.next उपयोग कर सकते हैं प्रत्येक उत्तरोत्तर तत्व प्राप्त करने के लिए है, लेकिन वास्तव में यह कैसे निष्पादन निलंबित और फिर सही जगह पर शुरू करने की जाती है?

मुझे पता है कि अगर 0.upto में उपज Fiber.yield के साथ प्रतिस्थापित की गई है तो यह समझना आसान है, लेकिन यह मामला यहां नहीं है। यह एक सादा पुराना yield है, तो यह कैसे काम करता है?

मैंने enumerator.c को देखा लेकिन यह मेरे लिए समझ में नहीं आता है। हो सकता है कि कोई व्यक्ति रुबी का उपयोग करके रुबी में कार्यान्वयन प्रदान कर सके, न कि 1.8.6 स्टाइल निरंतरता-आधारित एन्यूमरेटर, जो इसे स्पष्ट करता है?

उत्तर

12

यहाँ एक सादे गहरे लाल रंग का प्रगणक रेशे का उपयोग करता है और काफी की तरह मूल व्यवहार करना चाहिए कि मैंने सिर्फ मूल व्यवहार का अनुकरण किया।

उपयोग:

>> enum = MyEnumerator.new([1,2,3,4], :each_with_index) 
=> #<MyEnumerator:0x9d184f0 @f=#<Fiber:0x9d184dc> 
>> enum.next 
=> [1, 0] 
>> enum.next 
=> [2, 1] 
>> enum.to_a 
=> [[3, 2], [4, 3]] 
4

वास्तव में आपके ई = Bunk.new.each में अन्य खंड प्रारंभ में निष्पादित नहीं किया गया है। इसके बजाय 'if! Block_given' खंड निष्पादित करता है और एक गणनाकर्ता वस्तु देता है। गणनाकर्ता वस्तु आंतरिक रूप से एक फाइबर वस्तु रखती है। (कम से कम यह enumerator.c जैसा दिखता है।

जब आप ई.एच कहते हैं तो यह एक गणक पर एक विधि को कॉल कर रहा है जो आंतरिक निष्पादन संदर्भ को ट्रैक रखने के लिए आंतरिक रूप से फाइबर का उपयोग करता है। यह विधि फाइबर निष्पादन संदर्भ का उपयोग कर Bunk.each विधि को कॉल करती है। यहां बंक। प्रत्येक कॉल अन्य खंड को निष्पादित करता है और मूल्य उत्पन्न करता है।

मुझे नहीं पता कि उपज कैसे लागू की जाती है या कैसे फाइबर निष्पादन संदर्भ को ट्रैक करता है। मैंने उस कोड को नहीं देखा है। लगभग सभी गणक और फाइबर जादू सी

में लागू किया गया है क्या आप वास्तव में पूछ रहे हैं कि कैसे फाइबर और उपज लागू की जाती है? आप किस स्तर की जानकारी खोज रहे हैं?

यदि मैं आधार से बाहर हूं तो कृपया मुझे सही करें।

class MyEnumerator 
    include Enumerable 

    def initialize(obj, iterator_method) 
    @f = Fiber.new do 
     obj.send(iterator_method) do |*args| 
     Fiber.yield(*args) 
     end 
     raise StopIteration 
    end 
    end 

    def next 
    @f.resume 
    end 

    def each 
    loop do 
     yield self.next 
    end 
    rescue StopIteration 
    self 
    end 
end 

और अगर कोई प्रवाह नियंत्रण के रूप में अपवाद के बारे में शिकायत करने से पहले: असली गणनाकार भी अंत में StopIteration को जन्म देती है, इसलिए

+0

आपके उत्तर के लिए धन्यवाद। हां, मैं इस पर काफी विस्तार से पूछ रहा हूं। विशेष रूप से मैं जानना चाहता हूं कि रूबी में इसे लागू करना संभव है या क्या सी में कुछ चुपके चल रहा है जो रुबी में संभव नहीं है। यदि रूबी में इसे पूरी तरह कार्यान्वित करना संभव है, तो मुझे कोड देखना अच्छा लगेगा! :) – horseyguy

1

अन्य पोस्टर के रूप में बताया गया है, मैं इसे अपने स्वयं के निजी "फाइबर" [1.9 में] बनाता है विश्वास करते हैं। 1.8.7 (या 1.8.6 यदि आप बैकपोर्ट मणि का उपयोग करते हैं) किसी भी तरह या अन्य यह वही काम करता है (शायद क्योंकि 1.8 में सभी थ्रेड फाइबर के बराबर हैं, तो यह सिर्फ उनका उपयोग करता है?)

इस प्रकार 1.9 में और 1.8.x, आप उनमें से कई श्रृंखला अगर एक साथ a.each_line.map.each_with_index {}

यह वास्तव में एक तरह से कमांड लाइन पर एक पाइप की तरह, प्रत्येक लाइन के साथ कि पूरे श्रृंखला के माध्यम से बहती

http://pragdave.blogs.pragprog.com/pragdave/2007/12/pipelines-using.html

एचटीएच।

+1

यहां एक महान विस्तृत विवरण http://wiki.github.com/rdp/ruby_tutorials_core/enumerator है – rogerdpack

1

मुझे लगता है कि यह अधिक सटीक होगा।गणनाकर्ता पर प्रत्येक को कॉल करना मूल इटरेटर विधि को कॉल करने जैसा ही होना चाहिए। इसलिए मैं इस पर मूल समाधान थोड़ा बदल दूंगा:

class MyEnumerator 
    include Enumerable 

    def initialize(obj, iterator_method) 
    @f = Fiber.new do 
     @result = obj.send(iterator_method) do |*args| 
     Fiber.yield(*args) 
     end 
     raise StopIteration 
    end 
    end 

    def next(result) 
    @f.resume result 
    end 

    def each 
    result = nil 
    loop do 
     result = yield(self.next(result)) 
    end 
    @result 
    end 
end