range = 1..60
range.each_slice(range.last/4).with_index.with_object({}) { |(a,i),h|
h[a.first..a.last]=i }
#=> {1..15=>0, 16..30=>1, 31..45=>2, 46..60=>3}
The steps are as follows:
enum0 = range.each_slice(range.last/4)
#=> range.each_slice(60/4)
# #<Enumerator: 1..60:each_slice(15)>
You can convert this enumerator to an array to see the (4) elements it will generate and pass to each_with_index
:
enum0.to_a
#=> [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
# [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
# [31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45],
# [46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60]]
enum1 = enum0.with_index
#=> #<Enumerator: #<Enumerator: 1..60:each_slice(15)>:with_index>
enum1.to_a
#=> [[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 0],
# [[16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30], 1],
# [[31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45], 2],
# [[46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60], 3]]
enum2 = enum1.with_object({})
#=> #<Enumerator: #<Enumerator: #<Enumerator: 1..60:each_slice(15)>
# :with_index>:with_object({})>
enum2.to_a
#=> [[[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 0], {}],
# [[[16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30], 1], {}],
# [[[31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45], 2], {}],
# [[[46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60], 3], {}]]
Carefully examine the return values for the calculations of enum1
and enum2
. You might want to think of them as "compound" enumerators. The second and last element of each of enum2
's four arrays is the empty hash that is represented by the block variable h
. That hash will be constructed in subsequent calculations.
enum2.each { |(a,i),h| h[a.first..a.last]=i }
#=> {1..15=>0, 16..30=>1, 31..45=>2, 46..60=>3}
The first element of enum2
that is passed by each
to the block (before enum.each...
is executed) is
arr = enum2.next
#=>[[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 0], {}]
The block variables are assigned to the elements of arr
using parallel assignment (sometimes called multiple assignment)
(a,i),h = arr
#=> [[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 0], {}]
a #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
i #=> 0
h #=> {}
The block calculation is therefore
h[a.first..a.last]=i
#=> h[1..15] = 0
Now
h #=> {1..15=>0}
The calculations are similar for each of the other 3 elements generated by enum2
.
The expression
enum2.each { |(a,i),h| h[(a.first..a.last)]=i }
could alternatively be written
enum2.each { |((f,*_,l),i),h| h[(f..l)]=i }