Ruby #inject behavior is different from documentation - ruby

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?

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.

Related

Deeper explanation of reduce / inject method in ruby

I've been racking my brains around this for some time.
When using reduce - why is the first element returned without executing the operation defined in the block? Or am I missing a crucial point in how reduce works?
In the following example:
arr = [1, 3, 5]
arr.reduce {|sum, n| sum + (n * 3) }
#=> 25
I would have expected the result to be 27.
Since:
0 + (1 * 3) = 3
3 + (3 * 3) = 12
12 + (5 * 3) = 27
After some time playing around with it I figured out that in the first "tick" - the object from the array just get's added to the sum instead of being multiplied. So that the calculation is more like:
??? = 1
1 + (3 * 3) = 10
10 + (5 * 3) = 25
Could someone help me figure out where I've gotten off the path?
It's in the docs.
If you do not explicitly specify an initial value for memo, then the first element of collection is used as the initial value of memo.
I had a similar issue with the default values in Ruby inject/reduce methods, so I've tried to visualize it:
I thnk that the explanation is in the help o¡f the methos in this case:
[1] pry(main)> cd Array
[2] pry(Array):1> ? reduce
From: enum.c (C Method):
Owner: Enumerable
Visibility: public
Signature: reduce(*arg1)
Number of lines: 33
Combines all elements of enum by applying a binary
operation, specified by a block or a symbol that names a
method or operator.
The inject and reduce methods are aliases. There
is no performance benefit to either.
If you specify a block, then for each element in enum
the block is passed an accumulator value (memo) and the element.
If you specify a symbol instead, then each element in the collection
will be passed to the named method of memo.
In either case, the result becomes the new value for memo.
At the end of the iteration, the final value of memo is the
return value for the method.
If you do not explicitly specify an initial value for memo,
then the first element of collection is used as the initial value
of memo.
# 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
# find the longest word
longest = %w{ cat sheep bear }.inject do |memo, word|
memo.length > word.length ? memo : word
end
longest #=> "sheep"
So you need to specify the first memo as 0, in your case is one:
[4] pry(Array):1> [1,3,5].reduce(0) {|sum, n| sum + (n * 3) }
=> 27

Why do I get a 'typeerror' when using inject in Ruby?

I am using this inject method to make a running total of values into an array. I am trying to figure out why I am getting an error.
def running_totals(myarray)
results = []
myarray.inject([]) do |sum,n|
results << sum + n
end
results
end
p running_totals([1,2,3,4,5])
I am getting the error
in `+': no implicit conversion of Fixnum into Array (TypeError)
When breaking this method down, isn't this the same as adding two integers and adding that into an array? I'm a bit confused here. Thanks for the help.
In the first iteration sum will be an array (as you specified an array as the default when calling inject([])) and you try to add a number to it. in the results << sum + n statement
Instead, set the initial value to 0, then add, then add the result to the array, then make sure you let sum get passed into the next iteration of inject.
def running_totals(myarray)
results = []
myarray.inject(0) do |sum,n| # First iteration sum will be 0.
sum += n # Add value to sum.
results << sum # Push to the result array.
sum # Make sure sum is passed to next iteration.
end
results
end
p running_totals([1,2,3,4,5]) #=> [1, 3, 6, 10, 15]
The result of results << sum + n is an array results and it's this that's replacing the sum value and so the next iteration you're trying to add a fixnum n into an array sum ... plus it doesn't help that you're initializing the value of sum to be an array.
Make sure that the last executed statement in your inject block is what you want the accumulated value to be.
def running_totals(myarray)
results = []
results << myarray.inject do |sum, n|
results << sum
sum + n
end
results
end
p running_totals([1,2,3,4,5])
=> [1, 3, 6, 10, 15]
Note that I moved the result of the inject into results array as well, so that the final value is also included, otherwise you'd only have the four values and would be missing the final (15) value.
The return value of the inject block is passed as the first argument the next time the block is called, so those have to match. In your code, you're passing an array as an intital value, and then returning an array; so far, so good. But inside the code block you treat that array parameter (sum) as a number, which won't work. Try this:
def running_totals(myarray)
myarray.inject([]) do |results,n|
results << n + (results.last || 0)
end
end
The [] passed as an argument to inject becomes the first value of results; the first array element (1 in your example) becomes the first value of n. Since results is empty, results.last is nil and the result of (results.last || 0) is 0, which we add to n to get 1, which we push onto results and then return that newly-modified array value from the block.
The second time into the block, results is the array we just returned from the first pass, [1], and n is 2. This time results.last is 1 instead of nil, so we add 1 to 2 to get 3 and push that onto the array, returning [1,3].
The third time into the block, results is [1,3], and n is 3, so it returns [1,3,6]. And so on.
According to ri, you have to return result of the computation from inject's block.
From: enum.c (C Method):
Owner: Enumerable
Visibility: public
Signature: inject(*arg1)
Number of lines: 31
Combines all elements of enum by applying a binary
operation, specified by a block or a symbol that names a
method or operator.
If you specify a block, then for each element in enum
the block is passed an accumulator value (memo) and the element.
If you specify a symbol instead, then each element in the collection
will be passed to the named method of memo.
In either case, the result becomes the new value for memo.
At the end of the iteration, the final value of memo is the
return value for the method.
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.
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
# find the longest word
longest = %w{ cat sheep bear }.inject do |memo,word|
memo.length > word.length ? memo : word
end
longest
So your sample would work if you return computation result for each iteration, something like this:
def running_totals(myarray)
results = []
myarray.inject do |sum,n|
results << sum + n
results.last # return computation result back to Array's inject
end
results
end
Hope it helps.

Simple ruby loop to get sum of cubes

I was given this problem to solve with Ruby:
Compute the sum of cubes for a given range a through b. Write a method called sum_of_cubes to accomplish this task.
I wrote this:
def sum_of_cubes(a, b)
sum = 0
for x in a..b
c = x ** 3
end
sum += c
end
I got the value of the cube of b. What is wrong with this code? How can I solve this problem with a simple loop?
Thanks!
I would use Enumerable#reduce
def sum_of_cubes min, max
(min..max).reduce(0) { |a, b| a + b ** 3 }
end
A little explanation of what's happening here
We start with range (min..max) which is an Enumerable
irb> (1..3).is_a? Enumerable
=> true
Using the reduce instance method we get from Enumerable, we can use a block of code that gets called for each item in our (enumerable) range, and ultimately returns a single value.
The function name makes sense if you think "take my group of items and reduce them to a single value."
Here's our block
{ |a, b| a + b ** 3 }
We called reduce with 0 which is the initial value given to the block's a param
The return value of the block is passed to the block's a param on subsequent calls
Each item in the range will be passed to the block's b param
Let's step through and see how it works
(1..3).reduce(0) { |a, b| a + b ** 3 }
the first block call gets a=0 (initial value) and b=1 (first item in our range)
the return value of our block is 0 + 1 ** 3 or 1
the second block call gets a=1 (return value from the last call) and b=2 (the second item in our range)
the return value of our block is 1 + 2 ** 3 or 9
the third block call gets a=9 (return value from the last call) and b=3 (the third and last item in our range)
the return value of our block is 9 + 3 ** 3 or 36
the final return value of reduce is the last-called block's return value
in this case 36
You need to have sum += c inside the loop. And then return sum when done.
Here’s another way to calculate this. It doesn’t address your problems with your loop but I think it’s worth mentioning.
The sum of cubes of integers 13 + 23 + 33 + ... + n3 is given by the formula (n(n + 1)/2)2, so the sum of cubes of a given range min..max is therefore given by:
(max(max + 1)/2)2 - ((min-1)((min-1) + 1)/2)2
In code this could look like:
def sum_of_cubes_fixed min, max
lower = min - 1
(((max * (max + 1))/2) ** 2) - (((lower * (lower + 1))/2) ** 2)
end
This code avoids the loop, and so is O(1) rather than O(n) (almost – I’m hand waving a bit here, the time complexity of the multiplications and exponentiations will depend on the size of the numbers). For small sized ranges you won’t notice this, but for larger sizes the difference between this and the loop version becomes increasingly obvious. I haven’t done any strict benchmarks, but a quick test on my machine with the range 1 to 10,000,000 takes several seconds with the reduce method but is almost instantaneous with this method.
Normally I would just use reduce for something like this, but the structure of the task suggested that there might be a better way. With the help of Google I found the formula and came up with a more efficient solution (at least for large ranges).
(a..b).map{|i| i**3 }.inject(&:+) map and inject does the job, elegantly.
EDIT: Although it walks through the list twice ;)

Why ruby's inject doesn't sum correctly?

I'm not getting correct results from the following monkey-patching method in Integer:
def harm
1 + (2..self).inject{|sum, x| sum + 1/x.to_r}
end
2.harm #=> 3
it should return 3/2 instead, where is my mistake?
There are two problems here:
When you iterate across a closed range, such as 2..2, nothing actually happens:
(0..0).inject(){|s, x| s+= 99 }
# => 0
Which is why you get 3, as 1 + 2 is 3.
If you do not pass an argument into inject, it uses the first value you pass into the iterator as the starting memo, i.e. 2:
(2..2).inject(){|s, x| s+= 99 }
#=> 2
Putting in a 0 gets you an actual iteration:
(2..2).inject(0){|s, x| s+= 99 }
#=> 99
So try this instead in your method:
1 + (2..self).inject(0){|sum, x| sum + 1/x.to_r}
Standalone:
1 + (2..2).inject(0){|sum, x| sum + 1/x.to_r}
#=> 3/2
Here is the tip(you need to pass the initial value to the inject method):
def harm
1 + (2..2).inject(0){|sum, x| sum + 1/x.to_r}
end
harm # => (3/2)
Documentation of Enumerable#inject:
If you specify a block, then for each element in enum the block is passed an accumulator value (memo) and the element. If you specify a symbol instead, then each element in the collection will be passed to the named method of memo. In either case, the result becomes the new value for memo. At the end of the iteration, the final value of memo is the return value for the method.
If you do not explicitly specify an initial value for memo, then the first element of collection is used as the initial value of memo.
In the 1 minute of time that I decided to spend over your question, I was unable to realize what's wrong with your code. But I was able to write this method that does something similar to what you want to do:
class Integer
def harm
return 0 if self == 0
return -(-self).harm if self < 0
( 1 .. self ).map { |n| Rational 1, n }.reduce :+
end
end
0.harm #=> 0
2.harm #=> 3/2
7.harm #=> 363/140
-2.harm #=> (-3/2)
Note, though, that for large number, this highly readable code becomes inefficient, as it prepares the array in the memory before performing the summation.

Ruby: How to multiply several numbers?

I am trying to multiply any number of unknown arguments together to make a total.
def multiply(*num)
num.each { |i| puts num * num}
end
multiply(2,3,4)
multiply(2,3,4,5,6,7)
Another attempt:
def multiply(num)
num.to_i
i = 0
while i < num.length
total = num * num
return total
end
end
multiply(2,3,4)
multiply(2,3,4,5,6,7)
I keep running into errors:
(eval):73: undefined local variable or method `num_' for main:Object (NameError)
from (eval):81
Some saying that Array needs to be Integer.
I've tried doing something I thought was suppose to be very simple program to write.
def multiply(*num) captures all arguments given to the method in an array. If you run multiply(1, 2, 3, 4) then num will equal [1, 2, 3, 4]. So in both your attempts you're trying to multiply a whole array with itself (num * num), which is not going to work.
Others have suggested solutions to how to write the multiply method correctly, but let me give you a correct version of your first attempt and explain it properly:
def multiply(*numbers)
result = 1
numbers.each { |n| result = result * n }
result
end
As in your attempt I capture all arguments in an array, I call mine numbers. Then I declare a variable that will hold the result of all the multiplications. Giving it the value of 1 is convenient because it will not affect the multiplications, but I could also have shifted off the first value off of the numbers array, and there are other solutions.
Then I iterate over all the numbers with each, and for each number I multiply it with the current result and store this as the new result (I could have written this shorter with result *= n).
Finally I return the result (the last value of a method is what will be returned by the method).
There are shorter ways of doing the same thing, others have suggested using numbers.reduce { |n, result| n * result }, or even numbers.reduce(:*), but even though they are shorter, they are pretty cryptic and I assume they don't really help you get things working.
What you want is the #inject method
def multiple(*nums)
nums.inject(:*)
end
Inject will combine all of the elements in nums, using the operation specified ( * in this case ).
You can try this:
num.reduce(1) {|x, y| x*y }

Resources