We have a long description of it here: Derive #inject for a better understanding
To re-phrase: Enumerable#inject
takes two arguments: a base case and a block. Each item of the Enumerable
is passed to the block, and the result of the block is fed into the block again next time.
Walk through it:
[].inject(1) {|accumulator, item| accumulator + item }
The base case (1
) is the result on the empty list. The above produces 1
.
[2].inject(1) { |accumulator, item| accumulator + item }
This Enumerable has one element, 2
, so the block is run with the base case (1
) and the element (2
):
lambda { |accumulator, item| accumulator + item }.call(1, 2)
And the result is 3
.
[2,4].inject(1) { |accumulator, item| accumulator + item }
FIrst the block is called with 1
and 2
, as shown above, producing 3
. This 3
is then fed back into the block, along with 4
:
lambda { |accumulator, item| accumulator + item }.call(3, 4)
This produces 7
, which is the final result.
In the blog post linked above I give another way to think about it: recursively. Picture what an empty list class would look like:
class EmptyList
def inject(base, &block)
base
end
end
With no items in the list, it just produces the base case. On the other hand, imagine a class that represents the first item of the list and the rest of the list (for example, given [2,3,5,7,11]
, this object would be RecusiveList.new(2, [3,5,7,11])
):
class RecursiveList
def initialize(first, rest)
@first = first
@rest = rest
end
def inject(accumulator, &block)
new_accumulator = block.call(accumulator, @first)
@rest.inject(new_accumulator, &block)
end
end
In this case the first thing #inject
does is calculate the next accumulator by calling the block, passing in the prior accumulator in addition to the first value this object knows about. Then, since the only other thing this object has is an Enumerable, it calls #inject
on that Enumerable, giving this new accumulator as the base case.
Try walking through this example of the above to see how it functions:
list = RecusiveList.new(4, EmptyList.new)
list.inject(2) { |accumulator, item| accumulator * item }
Another way to think about it is in a more imperative style. This is how it’s implemented in the MRI Ruby code (in C).
class List
def initialize(enumerable)
@enumerable = enumerable
end
def inject(base, &block)
accumulator = base
@enumerable.each do |item|
accumulator = block.call(accumulator, item)
end
return accumulator
end
end
Again stepping through an example will be instructive. Try:
list = List.new([4, 9])
list.inject(2) { |accumulator, item| accumulator * item }