4

Taking a look at Ruby documentation on Enumerable class I noticed something interesting and I'd like to know why it happens this way.

At #inject description I've found these examples:

# Sum some numbers
(5..10).reduce(:+)                             #=> 45
# Same using a block and inject
(5..10).inject { |sum, n| sum + n }            #=> 45
# Multiply some numbers
(5..10).reduce(1, :*)                          #=> 151200
# Same using a block
(5..10).inject(1) { |product, n| product * n } #=> 151200

Notice that when #inject is used for multiplication, it receives an initial value of 1. I thought this was necessary because otherwise product would receive 0 as start value (as it happens in the sum) and the multiplication would also be 0. In fact, if I run

p (1..5).inject(0) { |prod, n| prod * n } 

I got

0

But then I run

p (1..5).inject { |sum, n| sum + n } 
p (1..5).inject { |prod, n| prod * n } 

and got

15
120

My questions are:

a) Why the documentation includes this 1 as initial value when, in fact, it is not needed?

and

b) What is the behavior of #inject when it comes to initializing the object being injected?

Ed de Almeida
  • 3,675
  • 4
  • 25
  • 57
  • What do you mean by initializing the object being injected ? – Saurabh Nov 30 '16 at 03:04
  • I mean, how does Ruby proceeds the initialization. You explained it in a certain sense in your answer below. – Ed de Almeida Nov 30 '16 at 03:06
  • 3
    Using the multiplicative identity (`1`) as the initial value in the `(5..10).reduce(1, :*)` example was a poor choice as `(5..10).reduce(:*)` produces the same result. Similarly, `(5..10).reduce(0, :+)` would be a poor and misleading example. – mu is too short Nov 30 '16 at 03:06
  • 1
    Yes, as I edited in my answer, it initialises the object as the first element of the array. – Saurabh Nov 30 '16 at 03:07
  • 1
    I think the point of including `1` in the example is to demonstrate how to (if required) pass an initial value/total etc. – Sagar Pandya Nov 30 '16 at 03:07
  • 1
    Agreed, @muistooshort. I see many poor examples in Ruby documents. – Ed de Almeida Nov 30 '16 at 03:07
  • Agreed, @sagarpandya82. The problem here was the poor choice of values. One is hardly a good value, since one can't see its effect in the multiplication. The example would be better without using the multiplicative identity, as it was pointed in other comment. – Ed de Almeida Nov 30 '16 at 03:11
  • @tokland makes a good point for why you might pass a multiplicative or additive identity [here](http://stackoverflow.com/a/40708047/5101493) – Sagar Pandya Nov 30 '16 at 03:45
  • Well noticed @sagarpandya82. – Ed de Almeida Nov 30 '16 at 03:50
  • Be careful with `reduce(1, :*)`. `[].reduce(1, :*) #=> 1` may not be what you want. Similar for `[].reduce(0, :+) #=> 0`. – Cary Swoveland Nov 30 '16 at 05:48

1 Answers1

8

To answer your first question:

a) Why the documentation includes this 1 as initial value when, in fact, it is not needed?

inject does not take 1 as initial value, from the apidock:

If you do not explicitly specify an initial value for memo, then uses the first element of collection is used as the initial value of memo.

Answer of second question lies in the answer of first itself, as it initialises the object as the first element of array on which inject is being applied.

Saurabh
  • 71,488
  • 40
  • 181
  • 244
  • 2
    It's also worth noting that it's often a good idea to seed in a default value to handle the case of an empty list: `[].inject(:+)` returns `nil`, but `[].inject(0, :+)` returns `0`. – tadman Nov 30 '16 at 06:43