pyb1993 pyb1993 - 5 months ago 18
Ruby Question

How does the enumerator receive a block?

I have:

letters = %w(e d c b a)


In the following:

letters.group_by.each_with_index { |item, index| index % 3 }
#=> {0=>["e", "b"], 1=>["d", "a"], 2=>["c"]}


how can the enumerator returned by
group_by
know the block it will execute? Is the block received by
each_with_index
passed to the enumerator which it is based on?

In the following:

letters.each_with_index.group_by { |item, index| index % 3 }
#=> {0=>[["e", 0], ["b", 3]], 1=>[["d", 1], ["a", 4]], 2=>[["c", 2]]}


will the block be passed to the enumerator returned by
each_with_index
? If it will, how does
each_with_index
execute it?

In general:


  1. How is a block retrieved by a method in an enumerator that doesn't directly receive the block?

  2. Will a block be passed through an enumerator chain? Where will it be executed?


Answer Source

There's some tricky stuff going on here, so that's probably why you're a little hazy on how it works. Enumerators are one of the most important things in Ruby, they're the backbone of the Enumerable system which is where Ruby really shines, but they're often used in ways where they're transparent, living in the shadows, so you rarely have to pay direct attention to them.

Looking more closely, step through this bit by bit:

letters.group_by
# => #<Enumerator: ["e", "d", "c", "b", "a"]:group_by>

Now this is an Enumerator instance. The each_with_index you chain on the end is actually an Enumerator-specific method:

letters.group_by.method(:each_with_index)
# => #<Method: Enumerator#each_with_index>

This is in contrast to your second approach:

letters.method(:each_with_index)
# => #<Method: Array(Enumerable)#each_with_index>

That one is Array's method which, conveniently, you can chain into a method like group_by.

So the story here is that group_by in chain mode actually provides special methods that have the effect of back-propagating your block to the group_by level.