Setting optional method arguments? - ruby

I am trying to define a method to sum the elements of a given array. So far I have:
def sum2
return self if self.length <=1
i = self.length
#sum2 = sum2(i-1) + self[i]
end
I'm getting an invalid argument error for the second call of sum2(i-1). Is there any way I can set it to take either 0 or 1 arguments? Or, am I going about this the wrong way?

It's not totally clear from the question phrasing, but I assume sum2 is a method in an array class that you've defined yourself. So, I assume it's derived from a Ruby Array class.
Currently, your method returns the array self if it has no more than one element. Technically, the array of one element isn't the sum of elements. So you don't want to return self in that case.
But you can use Ruby's array methods to simplify:
def sum2
self.inject(:+)
end
This will return nil if the array has zero length, and the sum of elements otherwise. If you want to return 0 on a length 0 array, then:
def sum2
return (self.length == 0) ? 0 : self.inject(:+)
end
Or more simply, per #toro2k's comment:
def sum2
self.inject(0, :+)
end

Related

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.

What does this Ruby code using inject method do?

class Array
def sum(start = 0)
inject(start, &:+)
end
end
Please explain me the use of this code.
Test case with input and output will be helpful.
Thank you.
It sums up all the elements that are kept inside you array. start is a value to which sum is added. for example for array foo = [1, 4]; foo.inject(10, &:+) will return 15 (10 + 1 + 4).
&:+ is telling what operation should be called on each element in array;
it's the same as you would give your own callback for example
foo.inject(10) do |current_sum, current_element|
current_sum = current_sum + current_element #can be written current_sum += current_element
current_sum
end

recursive bubble sort ruby

I am trying to build a recursive bubble sorting method in Ruby. It makes it through one pass fine, but it keeps returning after that. It seems that my
if array == swapped_array is being triggered, but I don't understand where my array variable is being redefined.
def bubble_sort(array, swapped = true)
return array if swapped == false
i = 0
if swapped == true
swapped_array = comparator(array)
end
if array == swapped_array
swapped = false
bubble_sort(array, swapped)
else bubble_sort(swapped_array)
end
end
def comparator(array, i = 0)
return array if i == array.length - 1
if array[i] > array[i+1]
array[i], array[i+1] = array[i+1], array[i]
end
i += 1
comparator(array, i)
end
swapped_array = comparator(array)
... takes the return value of comparator and assigns it to swapped_array. But comparator (eventually) returns the original argument array, which is the same array as the one defined in the caller. So
array == swapped_array
is always true.
If you want to compare two different arrays, you can call .dup on the array. This creates a new object with the same values as the original.
It is because in comparator, you are swapping the elements in the original array. Whatever you do to it, array and swapped_array in bubble_sort refer to the same array instance, and hence will always be the same. To solve the problem, add a single line to the top of your definition:
def comparator(array, i = 0)
array = array.dup
...
end

Define a method `sum_to_n?`

I have the code below. Method sum_to_n? takes an array arr of integers and an integer n as arguments and returns true if any two elements in arr sum to n. It should return true for empty arr with zero n, but keeps returning false.
def sum_to_n?(arr, n)
hash = Hash.new(0)
arr.each do |val|
if hash.key? val
return true
else
hash[n-val] = val
end
end
return false
end
What am I doing wrong?
shorter version:
def sum_to_n?(arr, n)
(arr.empty? && n.zero?) || arr.permutation(2).any? { |a, b| a + b == n }
end
Your code is (almost) correct, but your expectation is wrong. Your code returns true when there are two (or one) element that adds up to n. If you pass an empty array, then there will not be any element(s) that add up to n (because there is no element in the array in the first place). Hence, you get false.
If you want it to return true for an empty array, then that would be an exceptional behavior that does not follow logically. You will have to put a condition such as
return true if arr.empty?
in your code.

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.

Resources