2016-08-25 44 views
1
class MyString 
    include Enumerable 
    def initialize(n) 
    @num = n 
    end 
    def each 
    i = 0 
    while i < @num 
     yield "#{i} within while" 
     puts "After yield #{i}" 
     i += 1 
    end 
    end 
end 

s = MyString.new(10) 
a = s.to_enum 
puts "first" 
puts a.next 
puts "second" 
puts a.next 

我的红宝石版本是2.2.5,和代码的输出是Ruby如何实现枚举器#下一个方法?

第一
0内,而
第二
屈服后0
1内,同时

我认为执行流程是first a.next->s.each->while->yield->second a.next->jump into while loop 我的问题是如何实现Enumerator#next方法ented?

我大概知道有调用块的产量突破,这导致yield->second a.next;但是,我不明白第二个a.next如何跳回到while循环。

+0

我猜迷惑你是'yield'关键字,next'也不怎么'实现。 – halfelf

+0

@halfelf:我相信它非常关注'枚举器':在引入光纤之前,1.8是不可能的。普通的“yield”只适用于块,并且OP代码中没有块。这意味着捕获'tield'的块在'Enumerator'内部。 – Amadan

回答

4

我不明白第二个a.next如何跳回while循环。

魔法。 Enumerator的(和Fiber的)超级大国。

这两个类是在Ruby 1.9中引入的,并且有许多相似之处;特别是,它们允许您执行手动合作绿色线程。

让我们看看纤维第一,因为他们更基本:

f = Fiber.new do 
    puts "A" 
    Fiber.yield 1 
    puts "B" 
    Fiber.yield 2 
    puts "C" 
end 

puts "First" # First 
puts f.resume # A 
       # 1 
puts "Second" # Second 
puts f.resume # B 
       # 2 
puts "End"  # End 
f.resume  # C 
f.resume  # FiberError: dead fiber called 

基本上,纤维就像一个线程,但它会暂停时,它产生的Fiber.yield,并恢复时,它是由Fiber#resume恢复。它被用C语言实现为基本功能Ruby,所以作为Ruby的一名学生(相对于Ruby的学生解释器),你不需要知道它是如何工作的,只是它的确如此(就像你需要要知道IO#read会读取一个文件,但不一定在C中如何实现)。

Enumerator几乎是相同的概念,但适应迭代(而Fiber是更多用途)。事实上,我们可以把上面的几乎一模一样的字对字的相同与Enumerator

e = Enumerator.new do |yielder| 
    puts "A" 
    yielder.yield 1 
    puts "B" 
    yielder.yield 2 
    puts "C" 
end 

puts "First" # First 
puts e.next  # A 
       # 1 
puts "Second" # Second 
puts e.next  # B 
       # 2 
puts "End"  # End 
e.next   # C 
       # StopIteration: iteration reached an end 
+0

“基本上,纤维就像一根线” - 我会说它更像是一个半协程而不是线程。 –

+0

@JörgWMittag:AFAIK,光纤不像*半协程,它们*是*半协程。但是,很多人都知道线程,而半协程并不是众所周知的。 – Amadan

+0

谢谢。我知道了。 – RolandXu