The execution order of an Enumerator block in Ruby

In David Black, Well-founded Rubist I came across the following Ruby code for enumerations:

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 cannot wrap my head in the order of execution. I believe the result should have been simpler:

 Starting up the block! [1, 2, 3] Exiting the block! 

Any help would be greatly appreciated.

+5
source share
4 answers

You are surprised at this exit.

 Starting up the block! Exiting the block! [1, 2, 3] 

It is very simple. Comments illustrate what is happening.

 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 
+1
source

e.to_a

If you want to create an Array from your Enumerator, each element must first be extracted from it!

Ruby needs to make sure nothing is forgotten, and it needs to execute the entire block before returning the array. There may be another line that 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] 

Alternatives

Alternatively you can use:

e.next

 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

 e.each do |x| puts x end #=> # Starting up the block! # 1 # 2 # 3 # Exiting the block! 

e.map

If you want to create an array, but see the correct execution order, you can use:

 p e.map{ |x| px } 

It outputs:

 Starting up the block! 1 2 3 Exiting the block! [1, 2, 3] 
+4
source

I am reorganizing the last line a bit, so maybe this will be a little 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 above code is equivalent to the one given in your question. What happens is that the array is not printed in the Enumerator. The last line is the one who actually prints his value.

When e.to_a is e.to_a , the Enumerator block starts. It prints a start message, adds the values ​​to the y array y and prints the exit messages. When it finishes, the variable a will contain [1, 2, 3] , and the last puts finally prints it.

Hope this is a little better.

+1
source

If you want to make your code cleaner as expected, i.e. without using Ruby IO / File, (Ruby IO is the basis for all inputs and outputs in Ruby), then trying to execute it as suggested in the code below will give you a simple conclusion:

 e = Enumerator.new do |y| y << "start" (1..3).each {|i| y << i } y << "exit" end p e.to_a 

This will be your way out.

 # ["start", 1, 2, 3, "exit"] 

You can try such a methodology.

0
source

Source: https://habr.com/ru/post/1262803/


All Articles