Reputation: 3678
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
My ruby version is 2.2.5, and outputs of codes are
first
0 within while
second
After yield 0
1 within while
I think the execution flow is first a.next->s.each->while->yield->second a.next->jump into while loop
My question is how Enumerator#next method is implemented?
I probably know there are break in block yield invoked, which cause yield->second a.next
; however, I don't understand how second a.next can jump back into a while loop.
Upvotes: 1
Views: 144
Reputation: 198314
I don't understand how second a.next can jump back into a while loop.
Magic. Enumerator
's (and Fiber
's) superpowers.
These two classes were introduced in Ruby 1.9, and share many similarities; in particular, they allow you to do manual co-operative green-threading.
Let's look at fibers first, as they are more basic:
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
Basically, a fiber is like a thread, but it will pause whenever it yields by Fiber.yield
, and resume whenever it is resumed by Fiber#resume
. It is implemented in C as basic capability of Ruby, so as a student of Ruby (as opposed to student of Ruby interpreter) you don't need to know how it works, just that it does (just like you need to know IO#read
will read a file, but not necessarily how it is implemented in C).
Enumerator
is almost the same concept, but adapted for iteration (whereas Fiber
is more multi-purpose). In fact, we can write the above almost exactly word-for-word the same with an 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
Upvotes: 4