I am trying to understand the following Ruby code:
digits.each_with_index.inject(0) do |decimal, (digit, index)|
decimal + digit * 2**index
end
(For reference, digits
is a method that returns an array in which each element is an integer).
The part of the code that is confusing to me is .each_with_index.inject(0)
. I know what the each_with_index
method does, and I know what the inject
method does, but I am not sure how the chaining of both together is working. What exactly is going on?
I tried to look at the documentation for each_with_index
, and I guess the part that I am having trouble with is the following: "If no block is given, returns an enumerator."
I guess what all of this boils down to is, what is an enumerator?
An Enumerable is something the you can loop through. It is included in Array, Set etc.
From the docs: http://ruby-doc.org/core-2.2.2/Enumerable.html
The Enumerable mixin provides collection classes with several traversal and searching methods, and with the ability to sort. The class must provide a method each, which yields successive members of the collection. If Enumerable#max, #min, or #sort is used, the objects in the collection must also implement a meaningful <=> operator, as these methods rely on an ordering between members of the collection.
An Enumerator is something that you can use later to iterate through: http://ruby-doc.org/core-2.1.5/Enumerator.html An Enumerator, is a wrapper class which includes all the methods of Enumarable
In your example code, you're injecting a default value of 0, into the each_with_index loop, so on the first loop decimal
is equal to 0, digit is the first value of the array and index is 0. The second time through the loop, decimal is set to the return value of the first pass, digit is your second array value and index is 1.
For example:
digits = [20,30,40]
First time in the loop: decimal = 0, digit = 20, index = 0. This then returns 20.
Second time in the loop: decimal = 20, digit = 30, index = 1. This then returns 80.
Third time in the loop: decimal = 80, digit = 40, index =2. This then returns 240.
So this block returns 240.
An Enumerator abstracts the idea of enumeration so that you can use all of the handy Enumerable
methods without caring what the underlying data structure is.
For example, you can use an enumerator to make an object that acts kind of like an infinite array:
squares = Enumerator.new do |yielder|
x = 1
loop do
yielder << x ** 2
x += 1
end
end
squares.take(10)
# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
squares.count
# don't wait up for this one!
The cool thing about enumerators is that they are enumerable themselves and most Enumerable
methods return enumerators if you don't give them a block. This is what allows you to chain method calls to get one big enumerator.
Here's how I would code each_with_index
so that it can be chained nicely:
class Array
def my_each_with_index &blk
e = Enumerator.new do |yielder|
i = 0
each do |x|
yielder << [x, i]
i += 1
end
end
return e unless blk
e.each(&blk)
end
end
[3,2,1].my_each_with_index { |x, i| puts "#{i}: #{x}" }
# 0: 3
# 1: 2
# 3: 1
So first we create an enumerator which describes how to enumerate with indices. If no block is given, we simply return the enumerator. Otherwise we tell the enumerator to enumerate (which is what each
does) using the block.
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