As philomory said in a comment, this is a neat demonstration of some of Ruby's features, but also a horribly complex way of saying a + b
.
In it's most basic formulation, you could just do:
puts gets.chomp.to_f + gets.chomp.to_f
Of course, this leaves out a lot of the stuff your code is currently doing (like telling the user what they're supposed to input). So let's see if we can get all the same functionality, but with a different structure.
First, though, a few notes on your current code (apart from the metaprogramming):
Don't overwrite arguments just to avoid a new variable. You pass let
(a symbol) to define_method
, but then use it to store the user's chosen number. So now you have a variable named let
, presumably short for "letter", which has nothing to do with letters - now it's a floating point number. You have the contents (the input), but you've overwritten its label. Or, you have the answer, but you've forgotten the question, to paraphrase Hitchhiker's Guide.
Besides conflating the concepts of name and value, it's also impractical. You wouldn't, for instance, be able to print something like "You entered a = 23", because you no longer know both the letter and the entered value.
This is redundant
let = Float(gets.chomp)
sum += let.to_f
You create a Float
from the input. And then you call to_f
on that float - but it's already a float. Either use Float(...)
to get a number, or use the .to_f
on a string. The latter is the conventional approach, but the former will complain if the input isn't numeric, so that's probably the one you'll want to use here:
sum += Float(gets.chomp)
And we've avoided overwriting let
too.
But speaking of code complaining: You might want to be more specific in your exception handling. Your current rescue
block doesn't distinguish between the sorts of errors that might arise. It's one thing to complain to the user if their input is bad, but what if the error is about something else? For instance, if I run your code in my editor, it'll run without an interactive terminal. So gets
immediately returns nil
, which causes a NoMethodError
since chomp
isn't a method on nil
. And since gets
returns immediately, the script goes into a retry-loop, spewing "Something went wrong. Try again." as fast as it can until I kill it.
Obviously, this isn't how the script is meant to be run, but it goes to show how telling the user to "try again" isn't always a solution. Your code just assumes it's always the user's fault, and if it tries enough times, the problem will get fixed. But the error could also be a mistake in the code, like writing get
instead of gets
- that'd also cause the script to loop indefinitely, even though there's nothing the user can do to salvage things.
Now, for alternative approaches: While define_method
is a really neat Ruby feature, it's not called for in this case. But a generalized method for getting a float as user input would be useful. Then you can use that for the rest of the code. For instance, the script could be:
def read_float(prompt)
puts prompt
Float(gets.chomp)
rescue ArgumentError # only rescue this type of error
puts "Please enter a number"
retry
end
puts %w(a b).reduce(0) { |sum, var| sum + read_float("Enter #{var}") }
Yes, I'm being a little clever with the reduce
and everything. A more straight-forward and instantly readable way would be to say:
a = read_float("Enter a")
b = read_float("Enter b")
puts a + b
which is about as to-the-point of "A + B = C" as you can get.