问题
It's a simple problem given on rubeque.com: write a method that takes any number of integers and adds them to return true if the sum is 21. False otherwise. It tests the input with:
assert_equal twenty_one?(3, 4, 5, 6, 3), true
assert_equal twenty_one?(3, 11, 10), false
Here's what I have so far:
def twenty_one?(*nums)
nums.inject(&:+)
end
if twenty_one? == 21
puts true
else
false
end
But I get the error message:
RuntimeError: The value '21' does not equal 'true'.
I'm really confused on how to fix this. Is it possible to put the if/else statement within the method? And sorry if this question is really basic. I'm new to programming.
回答1:
You need to write your method as
def twenty_one?(*nums)
nums.inject(&:+) == 21
end
Here is one little demo :-
require "minitest/autorun"
class Numerics
def twenty_one?(*nums)
nums.inject(&:+) == 21
end
end
class TestNumerics < MiniTest::Test
def setup
@numeric = Numerics.new
end
def teardown
@numeric = nil
end
def test_twenty_one?
assert_equal @numeric.twenty_one?(3, 4, 5, 6, 3), true
assert_equal @numeric.twenty_one?(3, 11, 10), false
end
end
Let's run the tests :-
[arup@Ruby]$ ruby test/test_numerics.rb
Run options: --seed 61602
# Running:
.
Finished in 0.001332s, 750.9402 runs/s, 1501.8804 assertions/s.
1 runs, 2 assertions, 0 failures, 0 errors, 0 skips
[arup@Ruby]$
In your method, it was returning Fixnum
instance 21
, which you were trying to compare with true
. That's why you got the error. If you look at the source of assert_equal, you will find the comparisons between 2 objects which are the instances of the same class, otherwise it will throw the error you got.
Note: You of-course can write this nums.inject(&:+)
as nums.inject(:+)
too as Ruby allows this freedom in case of #reduce/#inject
method particularly out of the box.
Update
Carles Jove Buxeda gave one nice idea to design this problem. The idea is to put the methods inside the module, and then include it to test its methods:
require "minitest/autorun"
module Numerics
def twenty_one?(*nums)
nums.inject(:+) == 21
end
end
class TestNumerics < MiniTest::Test
include Numerics
def test_twenty_one?
assert_equal twenty_one?(3, 4, 5, 6, 3), true
assert_equal twenty_one?(3, 11, 10), false
end
end
Now if I run it :
arup_ruby$ ruby test/test_numerics.rb
Run options: --seed 1223
# Running:
.
Finished in 0.001067s, 937.2071 runs/s, 1874.4142 assertions/s.
1 runs, 2 assertions, 0 failures, 0 errors, 0 skips
arup_ruby$
Very cool idea!
回答2:
Arup's answer is great. I would suggest to make it a little more generic.
def twenty_one?(*nums)
nums.inject(&:+) == 21
end
def compare_value_with_sum_of_nums?(value, *nums)
nums.inject(&:+) == value
end
I am really bad with names. This made me think it would be pretty cool to combine your method name with metaprogramming and the gem humanize. You could then have dynamic method names and infer from the method name what value the sum should add up to. So you could have any comparison, like
MyAwesomeGem.three_thousand_and_twenty_four?(3000, 20, 4)
Would be fun to build!
:)
来源:https://stackoverflow.com/questions/28002853/ruby-adding-numbers-and-printing-true-when-sums-are-21