For some reason this code always multiplies b (2.54) by unit_number, regardless of whether I put enter "cm" or "in":
puts "Welcome to my unit conversion calculator. This calculator can convert
between Centimeters and Inches. If you could like to convert Centimeters to Inches, write: in. If you would like to convert Inches to centimeters, write: cm."
unit = gets.to_s
puts " how many of your unit would you like to convert"
unit_number = gets.to_f
a = 0.39370079
b = 2.54
if unit == 'in'
puts "your conversion is: #{ (unit_number * a)}"
else unit == 'cm'
puts "your conversion is: #{ (unit_number * b)}"
end
gets will capture the stdin input including the trailing new line (enter), so unit won't be 'cm' and instead will be "cm\n". You will need to remove the trailing "\n" using chomp:
unit = gets.chomp
You don't need to call to_s to the return value of gets because it is already a String, and the else statement should be elsif unit == 'cm' or else if unit == 'cm'.
Your conditional invokes an else statement, when what you actually need is an elsif statement:
elsif unit == 'cm'
Basically, what you currently have is a situation where the first of your conditions (i.e., if unit == 'in') is not being met (see Pablo B's answer for reasons why). However, the second condition is met because it's an else statement. Since the first condition is always false, the else condition will always be true.
If, instead, you invoke an elsif statement, the conditional will first check whether unit == 'in'; if it's not, then it checks whether unit == 'cm'. If neither are true, then the condition will return nil.
I know exactly how do you feel. Debugging is a pain. #akhanubis already pointed you towards #chomp method needed to process your gets return value. While I assume that you wrote your code for didactic purposes, if you ever need to convert physical units for real, just install sy gem. After gem install sy, you can:
require 'sy/imperial'
7.5.inch.in :cm
#=> 19.05
42.cm.in :inch
#=> 16.5354
Related
I have a small code for determining the number of hours per year.
What I am looking for is for a Ruby way to allow different user's input options for all possible valid answer stored in an array
to check if the user provided a valid option by calling include? on the array.
When year consist of 52 weeks.
Week consists of 7 days.
Day consists of 24 hours
My problem comes up when trying to pass different answers for the gets method such as follows:
if answer == "yes" || "yup" || "yeah" || "positive";
if answer == ("yes", "yup", "yeah", "positive")
if answer == ["yes" or "yup" or "yeah" or "positive"]
I receive an error which I couldn't solve
answer = gets.chomp
if answer == "yes" or "yup" or "yeah" or "positive"
puts "Good!"
puts desc_text
my_var = gets.chomp.to_i
if my_var == 3736
puts good_text
else
puts wrong_text
puts bad_text
end
elsif answer == [ "no" || "nop" || "nay || ""negative" ]
puts bad_text
else
puts yes_no
end
I'd like to pass different answer options such as yes, yup, yeah, positive instead of just enclose me to a yes and/or no answer
A more idiomatic Ruby way of allowing for these different user options would be to have all possible valid user input stored in an array, then checking if the user provided a valid option by calling include? on the array. This way, you can update the array with more possible options at a later time. Generally arrays like this are stored as constants, though you can also store them as local variables. I find that for a script like this, variables are fine, but if I'm writing a larger program and I need arrays like this (for example, arrays that specify valid options) in a class, then I use constants to make it easy for other contributors to find them. Another option yet would be to have a configuration file (can be a Ruby file, yaml file, JSON file...) that defines valid options, and then you can load this file when executing your program.
Here is an example of what I mean:
VALID_AFFIRMATIVE_ANSWERS = %w(yes yup yea positive)
VALID_NEGATIVE_ANSWERS = %w(no not nay negative)
answer = gets.chomp
if VALID_AFFIRMATIVE_ANSWERS.include?(answer)
puts "Good!"
puts desc_text
my_var = gets.chomp.to_i
if my_var == 3736
puts good_text
else
puts wrong_text
puts bad_text
end
elsif VALID_NEGATIVE_ANSWERS.include?(answer)
puts bad_text
else
puts yes_no
end
I'm not sure what errors you're receiving, but I'm not sure if you're ever defining some of these variables (for example desc_text etc.).
if code is excetly as you write above your issue is in line written below
elsif answer == [ "no" || "nop" || "nay || ""negative" ]
although it won't work , so the solution for you would be like below
def process_positive_response
my_var = gets.chomp.to_i
if my_var == 3736
puts good_text
else
puts wrong_text
puts bad_text
end
end
answer = gets.chomp
case answer
when 'yes','yup','yeah','positive'
puts "Good!"
puts desc_text
process_positive_response
when 'no', 'nop', 'nay', 'negative'
puts bad_text
else
puts yes_no
end
first why write so much conditions when ruby already does that for you with case statement, and second distribute your code in chunks so it will make your code more traceable.
I have this ruby code for my class player and my class game, I'm working in the tennis kata.
class Player
def score
if #player_one.points <3 and #player_two.points<3
"#{#player_one.score} #{#player_two.score}"
elsif #player_one.points == #player_two.points
"#{#player_one.score} iguales"
end
if #player_one.points == 3 and #player_two.points == 3
'deuce'
elsif (#player_one.points == #player_two.points + 1) and (#player_two.points >=3)
"Ventaja #{#player_one.name}"
elsif (#player_one.points >= 4) and (#player_one.points >= (#player_two.points + 2))
"Game #{#player_one.name}"
end
end
end
And this test can't pass, all the other tests that had already passed aren't here.
it 'return 30-iguales when both players have 2 points scored' do
player_one = Player.new
player_two = Player.new
game = TennisGame.new(player_one, player_two)
player_one.wins_point
player_one.wins_point
player_two.wins_point
player_two.wins_point
expect(game.score).to eq("30 iguales")
end
The error that I got is that "30 iguales" are expected but got nil.
All my other tests have passed, but in this one fails, and I cant solve the problem.
Ruby methods will return the last evaluated expression even without an explicit return statement, and I take it that’s what you’re going for here. The problem is that for the test data the method will evaluate "#{#player_one.score} iguales", but then continue on evaluating the second if statement, and falling all the way through it to return nil.
Your options to fix are:
Make the whole thing a single if statement instead of two. Or:
Explicitly return the value of each possible string.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
Well when Input a number, it doesn't print out any of the strings for the given value. if i put in 100, shouldn't it print "go to server room"?
stress = gets.chomp!
def stress
if stress === 100
p "Go to server room"
elsif stress > 50
puts "Take a break"
else
p "Continue working"
end
end
p stress
if i put in 100, shouldn't it print "go to server room"?
I assume that you want your program to read user input and then print different messages depending on that input.
[I'm] new to Ruby
Okay, so first of all, Ruby doesn't do implicit type conversion like JavaScript or PHP. Or not to the same extent. It does allow you to compare floats and integers:
1.0 == 1 #=> true
but comparing strings and integers doesn't work that way:
'1' == 1 #=> false
You have to convert it yourself:
'1'.to_i == 1 #=> true
Since your input is supposed to be an integer, we can convert it right away:
stress = gets.to_i
When entering 100enter, gets will return "100\n" and to_i will interpret that as an integer, returning 100 (to_i ignores the trailing newline, so we don't need chomp).
stress is now an integer, so you can compare it to other integers:
if stress == 100
puts "Go to server room"
elsif stress > 50
puts "Take a break"
else
puts "Continue working"
end
I've changed === to ==. For comparisons you almost always want the latter. === has special meaning in Ruby. Just avoid it for now.
The above code would already work, but you wanted a method. You could call your method stress, i.e. use the same name for the variable and the method. But it makes your code much harder to understand. So let's choose a different method name, e.g. message. While we're at it, let's also remove the repeated puts calls, so it just returns the message without printing it:
def message(stress)
if stress == 100
"Go to server room"
elsif stress > 50
"Take a break"
else
"Continue working"
end
end
You can call the above method via:
input = gets.to_i
puts message(input) # <- 'puts' is now here
I've deliberately chosen a different variable name for the input to distinguish it from the method argument.
You have a few problems here. For one, as someone already mentioned, you are not passing an argument to your method. For another, the gets method returns a String, not an integer, so if you pass the result in you are comparing a String to an integer.
Rework your method like this:
def stress(level_string)
level = level_string.to_i
if level == 100
"Go to server room"
elsif stress > 50
"Take a break"
else
"Continue working"
end
end
Now you can try:
>> level_string = gets.chomp!
>> 100
#> "100"
>> stress(level_string)
#> "Go to server room"
In your statement
p stress
you output the value of the variable named stress, not the value returned by the method stress.
You can have a method and a variable of the same name, but then you have to invoke the method using parenthesis:
p stress()
In your particular case, you would then run however into the next problem: Inside your method stress, you are in a nested scope, which means that the outside variable stress is not visible. Hence, in the statement
if stress === 100
you refer to the method stress, i.e. you have a recursion.
There are several possible solutions:
Pass the variable by parameter
Change the name of the variable
Change the name of the method
As a rule of thumb, I recommend to
never give a variable and a method the same name, and
do not access variables from outer scope inside a method
unless you have very good reasons to do otherwise.
Your variable and your method definition have the same name. Try changing:
stress = gets.chomp!
to
var = gets.chomp!
Then set up your method to accept the variable:
def stress(s)
if s.to_i == 100
p "Go to server room"
elsif s.to_i > 50
puts "Take a break"
else
p "Continue working"
end
end
p stress(var)
You can also do it close to what you were doing, just use a variable other than the name of your method:
var = gets.chomp!
def stress
if self.to_i == 100
p "Go to server room"
elsif self.to_i > 50
puts "Take a break"
else
p "Continue working"
end
end
p var.stress
I am writing a little practice program with if and else. The code is as follows:
puts "What is your name?"
user_name = $stdin.gets.chomp
print "Hello #{user_name}! Welcome to Puzzles and Riddles v.1!"
puts "There are two doors \n 1. Riddles \n 2. Puzzles. \n Which door do you go through?"
answer_1 = $stdin.gets.chomp
if
answer_1 == "1"
puts "You have taken the Riddle room!"
print "Here is your riddle: \n You use a knife to slice my head and weep beside me when I am dead. \n What am I?"
end
riddle_1_answer = $stdin.gets.chomp
if
riddle_1_answer == ( riddle_1_answer == "An onion" ) || ( riddle_1_answer == "an onion" ) || ( riddle_1_answer == "Onion" ) || ( riddle_1_answer == "onion" )
puts "The correct answer is: An onion! \n You have advanced to round two."
else
puts "Sorry, your answer is incorrect. Think about it."
end
puts "Riddle 2. \n What has 4 fingers and a thumb, but is not living?"
riddle_2_answer = $stdin.gets.chomp
Now if the user got riddle_1_answer wrong how would I make it so that the program exits/aborts?
I tried adding exit(0) to the else section and it would terminate the program but would also come up with an error. So I'm not sure if the error is causing the program to end or its the exit(0) command
It's obvious from your question, the sample code, and your answers to #AndrewMarshall, that you need some guidance.
Here you go.
First, ruby is one of many programming languages, and there is a reason why many experienced programmers end up gravitating to it: ruby is object-oriented, expressive, powerful, and reasonably concise without being unnaturally terse. So, if you are going to learn ruby, learn by reading lots of good ruby code, from which you'll learn good practices.
Second, appearance matters because it impacts or enhances readability and comprehension. A good appearance lends to improved readability and more rapid comprehension. A bad appearance does the opposite.
The lack of alignment of the if, else, and end tokens in your code is bad; it made it hard to see the structure of the code's logic.
There are many rules of thumb in programming. Here are a few such rules that apply to most languages:
use alignment and indention properly
always think about "edge cases" (or errors)
limit and isolate complexity (use functions, modules, classes, & methods)
Don't Repeat Yourself (DRY)
So let's apply those two principles to your code and transform it a bit.
The first two lines:
puts "What is your name?"
user_name = $stdin.gets.chomp
What if the user enters CTRL-D (EOF)?
What if the user enters an empty line?
Is an error acceptable? An EOF on STDIN returns a nil, which causes an error on the chomp.
Is an empty string (zero length) name acceptable? If not, what should we do about it?
When there are complexities on doing something relatively simple, like getting a user name, encapsulate the complexities in a function or method, so the code needing the user name is not made confusing by the complexities of getting it.
Here's a replacement. First, let's manage the details (and complexities) of getting a user name within a small function.
def get_user_name
name = ''
while name.size == 0 do
print "What is your name? "
name = gets
exit(1) if name.nil? # exit program on EOF
name.strip!
end
name
end
Notice that we don't use chomp on name until after we've made sure that it isn't nil. Many programs react to EOF on input by exiting, aborting, or continuing on without any more questions. In this example, we'll just assume the user wants to quit.
Notice also that we used strip! instead of chomp!? That's because strip! will remove both leading and trailing whitespace, including the trailing newline.
Notice also that we didn't use $stdin.gets, but instead just gets? This is because the default object for gets is $stdin.
Probably a better approach to managing exceptional situations within small functions (methods), is to raise an exception, and let the higher level application logic decide how to manage it. With that in mind, here is a revised definition:
def get_user_name
name = ''
while name.size < 1 do
print "What is your name? "
name = gets
raise "End of input" if name.nil? # raise exception on EOF
name.strip!
end
name
end
Now, that get_user_name is defined, we can use it wherever we need a user name. We know that EOFs are managed, and we know that we won't get an empty string.
user_name = get_user_name
Now, let's do the rest of your original code, but aligned and indented properly.
print "Hello #{user_name}! Welcome to Puzzles and Riddles v.1!"
puts "There are two doors \n 1. Riddles \n 2. Puzzles. \n Which door do you go through?"
answer_1 = $stdin.gets.chomp
if answer_1 == "1"
puts "You have taken the Riddle room!"
print "Here is your riddle: \n You use a knife to slice my head and weep beside me when I am dead. \n What am I?"
end
riddle_1_answer = $stdin.gets.chomp
if riddle_1_answer == ( riddle_1_answer == "An onion" ) || ( riddle_1_answer == "an onion" ) || ( riddle_1_answer == "Onion" ) || ( riddle_1_answer == "onion" )
puts "The correct answer is: An onion! \n You have advanced to round two."
else
puts "Sorry, your answer is incorrect. Think about it."
end
puts "Riddle 2. \n What has 4 fingers and a thumb, but is not living?"
riddle_2_answer = $stdin.gets.chomp
Now that the alignment and indentation is correct, it's easier to see the logic, and its flaws. It's also easier to see a pattern of logic, and whenever you see a pattern, then DRY it up, and make methods (functions) to encapsulate the repetition.
But first, let's fix the obvious bugs.
The if expression is broken. Look at it again, and you'll see this:
if riddle_1_answer == TEST1 || TEST2 || TEST3 || TEST4
where I've used TESTn to replace the various case-sensitive tests you had.
This if expression will always fail because the value of riddle_1_answer will never be true or false and the result of the various TESTn expressions will always be true or false. I'm pretty sure you wanted this:
if TEST1 || TEST2 || TEST3 || TEST4
Second, when testing for a string value, it's not necessary to test all case variations. Just downcase the answer and test on lowercase test values (unless case-sensitivity is important). Alternatively, if simple character string tests aren't sufficient, then use a regular expression and use the i option for case-insensitive matching. Example:
if riddle_1_answer =~ /(?:an )?onion/i
Will test for "an onion" or "onion" in upper, lower, and mixed case.
Perhaps more important than these little errors, one should look to avoid repetition. The general pattern appears to be:
Ask a question
Accept an answer
Check the answer
Change the program state based on the answer
Repeat
When you see things like this, you should start thinking of arrays and hashes. Arrays are used when the values can be numerically indexed, and hashes are used when you want to get values associated with varying keys. Then, a simple loop can be used to iterate across the values of the array or hash.
So, seeing the pattern above, it becomes more clear that we will need a method to prompt for the question, get the answer, deal with a possible EOF and empty strings, validate non-empty answers, possibly repeating the question & answer when needed.
So let's define a little method to get an answer
# prompt_and_get_answer PROMPT, ANSWERS_DATA
#
# issue PROMPT, and get an answer, which must be one of the
# values in ANSWERS_DATA array, or one of the keys of the
# ANSWERS_DATA hash.
def prompt_and_get_answer prompt, answers_data
ans = ''
while ans.size < 1
print prompt
ans = $stdin.gets
if ans.nil?
raise "End of input"
end
ans.strip!
if answers_data.class == Hash # hash?
answers = answers_data.keys.sort
else
answers = answers_data.sort
end
matches = answers.grep(/#{ans}/i) # match possible valid answers
case matches.size # how many items found?
when 0
puts "#{ans} is not a valid answer. Use one of:"
puts answers.join(', ')
ans = ''
when 1 # return the match or the value of the matching key
ans = answers_data.class == Hash ? answers_data[matches[0]] : matches[0]
else
puts "#{ans} is ambiguous; be more specific to match one of:"
puts answers.join(', ')
ans = ''
end
end
ans
end
Now, we can use this method with a prompt as an argument, and get an answer, knowing that it's not going to be an empty answer and it's not going to be an EOF.
First, let's name the questions, and let's show the prompts as they would appear on output, without using the explicitly escaped newlines (\n).
$prompt1 = <<EOQ
There are two doors
1. Riddles
2. Puzzles
Which door do you go through?
EOQ
$answer1 = [ '1', '2' ]
$prompt2 = <<EOQ
Here is your riddle:
You use a knife to slice my head and weep beside me when I am dead.
What am I?
EOQ
$answer2 = [ 'onion' ]
ans1 = prompt_and_get_answer $prompt1, $answer1
if ans1 == '1'
do_riddles
elsif ans1 == '2'
do_puzzles
else
raise "Bad answer to prompt1"
end
def do_riddles
while true
ans = prompt_and_get_answer $prompt2, $answer2
if ans == 'onion'
puts "yay! you're right!"
break
else
puts "nope. Try again"
end
end
end
You can see from this that we've used methods (functions) to break the logic into smaller pieces, and to separate the details of getting answers from the details of testing them.
Later, we can see how to use tables and hashes to build up a list of questions.
Good luck.
I am trying to halt input from a user when their input is 42.
The correct answer on the website I'm working on is:
while line = gets
break if (/42/ =~ line)
x << line
end
The code I tried that does not work is:
while line = gets.chomp
break if (line == 42)
x << line
end
Why is this the case? Am I missing some limitations to what I can use in my if statement?
The problem is that 42 is an integer, but line is a string:
1.9.3p392 :001 > "42" == 42
=> false
So it's never the case that your if statement is getting triggered, because it's comparing two different kinds of things. Matching with a Regex fixes it, though it's looking for "42" to appear anywhere in the input (e.g. "3427"). I think what you meant to say was
while line = gets.chomp
break if (line == "42")
x << line
end
In other words, break when the input is a string with the characters 4 and 2 in it.
I suspect it's because you're comparing a number to a string. The example uses a regular expression it appears. "42" == 42 will give you false in ruby.
<< is a method(Append) on Array or String class objects. But your x not holding any referencing to such objects. Thus you are getting undefined local variable or method 'x' for main:Object (NameError).
Try this instead(by fixing local variable x to hold a practical object and converting line value to Fixnum object:
x = "hi"
while line = gets.chomp
break if (line.to_i == 42)
x << line
end
This program will help you to halt input from a user when their input is 42.
until (line = gets.chomp).to_i == 42
x << line
end
This of course bypasses the if statement you were asking about.
Your limitation for the if is based solely on the fact that you are comparing a string that will always be a string to a number, and this will never be equal. (as others have mentioned)
So we must reconsider the conditional statement. In this case, I considered it "out of place" and moved the comparison to the 'while' loop, and then inverted it to an 'until' statement, to be able to positively express the condition to end the loop on. Whenever I see a 'break' in a loop, I try to get rid of that smell, as the condition to leave a loop should really be expressed in the loop condition if possible.
I hope this helps.