In David Black's The Well-Grounded Rubyist I came across the following Ruby code about enumerators:
e = Enumerator.new do |y|
puts "Starting up the block!"
(1..3).each {|i| y << i }
puts "Exiting the block!"
end
p e.to_a
which returns the following output:
Starting up the block!
Exiting the block!
[1, 2, 3]
What bothers me the most is that I can't wrap my head around the order of execution. I believe the output should have been the more straightforward:
Starting up the block!
[1, 2, 3]
Exiting the block!
Any help would be much appreciated.
If you want to create an Array out of your Enumerator, every element must first be extracted out of it!
Ruby needs to make sure that nothing is forgotten, and needs to execute the whole Block before returning an Array. There could be another line which defines more elements :
e = Enumerator.new do |y|
puts "Starting up the block!"
(1..3).each {|i| y << i }
puts "Exiting the block! (Not really)"
(4..6).each {|i| y << i }
puts "Exiting the block!"
end
It outputs :
Starting up the block!
Exiting the block! (Not really)
Exiting the block!
[1, 2, 3, 4, 5, 6]
As alternatives, you could use :
p e.next
p e.next
p e.next
p e.next
It outputs :
Starting up the block!
1
2
3
Exiting the block!
enum.rb:11:in `next': iteration reached an end (StopIteration)
e.each do |x|
puts x
end
#=>
# Starting up the block!
# 1
# 2
# 3
# Exiting the block!
If you want to create an Array but still see the correct execution order, you can use :
p e.map{ |x|
p x
}
It outputs :
Starting up the block!
1
2
3
Exiting the block!
[1, 2, 3]
You are surprised about this output.
Starting up the block!
Exiting the block!
[1, 2, 3]
It's very simple. Comments will illustrate what's going on.
e = Enumerator.new do |y|
# print first message
puts "Starting up the block!"
# append elements to array y (but don't print it)
(1..3).each {|i| y << i }
# print second message
puts "Exiting the block!"
end
# print the array
p e.to_a
I will slightly refactor the last line so maybe it's a bit more clear:
e = Enumerator.new do |y|
puts "Starting up the block!"
(1..3).each {|i| y << i }
puts "Exiting the block!"
end
a = e.to_a
# At this point "Starting up the block!" and "Exiting the block!"
# have been printed.
# Also the value of a is [1, 2, 3].
puts a # Prints [1, 2, 3]
The code above is equivalent to the one in your question. What is going on is that the array is not printed by the Enumerator. The last line is the one who actually prints its value.
When e.to_a
is executed, the Enumerator block will run. It will print the start message, add the values into the y
array and print the exit messages. When it finished, the a
variable will contain [1, 2, 3]
and the last puts
will finally print it.
Hopefully this makes a bit more sense.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With