I'm attempting Problem 6 in Project Euler in Ruby (in my attempt to learn the language), and here is what I came up with in the first iteration:
upto = 10
a = (1..upto).to_a.product((1..upto).to_a)
#a.each{ |x| print "(#{x[0]}, #{x[1]})\n"}
puts a.inject(0) {|sum, x| sum + x[0]*x[1] if (x[0] != x[1])}
Unfortunately, that throws up the following error on Ruby 2.0:
in
block in <main>': undefined method
+' for nil:NilClass (NoMethodError)
Even more puzzling is that the error is not encountered when I remove the if conditional (which obviously gives me the wrong answer btw!)
upto = 10
a = (1..upto).to_a.product((1..upto).to_a)
a.each{ |x| print "(#{x[0]}, #{x[1]})\n"}
puts a.inject(0) {|sum, x| sum + x[0]*x[1]} #if (x[0] != x[1])}
The above gives the following output (after printing out the elements of a):
3025
As a debugging step, I've even printed out the contents of 'a', to ensure that there are no nil elements - that has turned out fine. Could someone explain
EDIT: It would also be nice to get comments on alternative, more elegant ways to achieve the same solution, as I'd like to know the standard way a Rubyist would solve this!
That's because you are passing nil
in the case of x[0] != x[1]
to the inject block. The return value of this block, is the new value of the accumulator value (sum
), thus, if there's no change, just return sum
. Otherwise the new value of sum
is nil
, for which nil.respond_to(:+) #=> false
on the following iteration, leading to the error you've encountered.
n = 10
a = (1..n).to_a.product((1..n).to_a)
puts a.inject(0) {|sum, x| x[0] == x[1] ? sum + x[0] * x[1] : sum }
As I'm sure you know, the value of sum
is whatever the block evaluated to on the previous expression (or the initial value supplied on the first time)
What your code should do is
if x[0] != x[1]
sum + x[0]*x[1]
else
sum
end
Your code effectively omits the else, so when the condition is not met, your block evaluations to 0
You might also want to know that
(1..upto).combination(2).to_a
Gives you the array of non repeated pairs directly so that you don't need your if statement in inject
You could even do
(1..upto).to_a.combination(2).collect {|pair| 2* pair[0] * pair[1]}.inject(:+)
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