If this code executes the else statement, I want it to retry either from rescue or from begin.
Running the code asks me for an input and, when I input it, the code doesn't work.
1- What can I do to make this code work with the else statement running the retry?
2- Why does removing rescue create a retry Syntax Error?
# Creates input method
def input
x = gets.to_i
end
#Begins the first choice
begin
puts "What will you do?
1- Get a closer look
2- Go in the opposite direction
Write your input an press enter:"
rescue
#Calls input method
choice = input
if choice == 1
puts "You get a closer look and..."
elsif choice == 2
puts "You go in the opposite direction, out of trouble"
else
puts "Incorrect input, enter a number between the one's avaliables:"
end
#Retries if the choice is error
retry if choice != 1||2
end
Using a rescue block is for handling exceptions, I really don't think it's needed here. A loop will do the job.
puts "What will you do?",
"1- Get a closer look",
"2- Go in the opposite direction",
"Write your input and press enter:"
loop do
choice = gets.to_i
if choice == 1
puts "You get a closer look and..."
break
elsif choice == 2
puts "You go in the opposite direction, out of trouble"
break
else
puts "Incorrect input, enter a number between the ones available:"
end
end
Related
I'm doing a pwd generator in ruby and when I get to a certain point of the code I need to return back if the user says that he want to retry to generate the pwd.
print "do you want to retry to generate the password? [y/n]"
retrypwd = gets.chomp
if retrypwd == y
(code to jump to some lines ago)
elsif retrypwd == n
print "Ok, It'll be for the next time"
end
The trick is to use a loop and break it or repeat it according to your expectations:
def try_again?
loop do
print "Would you like to try again? Y/N"
again = gets.chomp.capitalize
case (again)
when 'N'
return false
when 'Y'
return true
else
puts "Huh? I don't know what that means."
end
end
end
Then you can incorporate this into your main program:
begin
try_password
end while try_again?
You will keep trying passwords until try_again? returns false, which happens if you type "N".
I have a program that displays a numbered list and asks the user to input either a number or name from the list, and loops a block until the user enters "exit", after which it ends.
I want to add a line or two that puts an error message like, "Sorry, I don't seem to understand your request" if the user inputs something that is not on the list (name/number) and is not the word "exit".
I can't seem to figure it out. Any advice? My current code is below.
def start
display_books
input = nil
while input != "exit"
puts ""
puts "What book would you more information on, by name or number?"
puts ""
puts "Enter list to see the books again."
puts "Enter exit to end the program."
puts ""
input = gets.strip
if input == "list"
display_books
elsif input.to_i == 0
if book = Book.find_by_name(input)
book_info(book)
end
elsif input.to_i > 0
if book = Book.find(input.to_i)
book_info(book)
end
end
end
puts "Goodbye!!!"
end
Seems that you should add an elsif statement in this if:
if book = Book.find_by_name(input)
book_info(book)
elsif input != 'exit'
puts "Sorry, I don't seem to understand your request"
end
A good template for an interpreter is to build around Ruby's very capable case statement:
loop do
case (gets.chomp.downcase)
when 'list'
display_books
when /\Afind\s+(\d+)/
if book = Book.find($1.to_i)
book_info(book)
end
when /\Afind\s+(.*)/
if book = Book.find_by_name($1)
book_info(book)
end
when 'exit'
break
else
puts "Not sure what you're saying."
end
end
Although this involves regular expressions, which can be a bit scary, it does give you a lot of flexibility. \A represents "beginning of string" as an anchor, and \s+ means "one or more spaces". This means you can type in find 99 and it will still work.
You can create a whole command-line interface with it if you take the time to specify the commands clearly. Things like show book 17 and delete book 17 are all possible with a bit of tinkering.
World!
New to all types of programming, so maybe I'm not understanding how loops/if/ else work in general?
My problem is this:
Any time I run this program, it runs fine, except for the exiting part, aka saying "BYE".
Whenever I say "BYE" grandma responds with "NO, NOT SINCE 1943" then the program exits.
My problem is that If I move the statement = gets.chomp to anywhere else besides the inside of the two ends it infinite loops, or gives me the same exiting issue.
This is the code I am using.
puts "Talk to grandma!"
puts "To stop talking to Grandma say 'BYE'"
sleep 1
puts "Remember grandma has bad hearing. You might need to Yell!"
statement = ""
while statement != "BYE"
statement =gets.chomp
if statement != statement.upcase
puts "WHAT?! SPEAK UP!"
else
year = rand(1940..1955).to_s
puts "NO, NOT SINCE " + year
end
end
Problem: Exits when "BYE" is spoken, but only after saying "NO, NOT SINCE" etc etc
However when I change the code around to:
puts "Talk to grandma!"
puts "To stop talking to Grandma say 'BYE'"
sleep 1
puts "Remember grandma has bad hearing. You might need to Yell!"
statement = gets.chomp
while statement != "BYE"
if statement != statement.upcase
puts "WHAT?! SPEAK UP!"
else
year = rand(1940..1955).to_s
puts "NO, NOT SINCE " + year
end
statement= gets.chomp
end
The program then works PERFECTLY.
Doesn't the loop end when it says end? How would the statement = gets.chomp in the bottom get included in the loop if it already ended? Or am I misunderstanding it, and the first end relates to the if/else, and the SECOND end refer to the "ending of the loop"?
I'm self learning with Chris Pines Learn to Program using Ruby, but... all it says is "Everything in the loop before end gets repeated."
Former code:
while statement != "BYE"
statement =gets.chomp
# if-else validation
end
Which means that you do these steps:
Read user input
Validate it with your if-else statement
Continue with loop.
Validate it against the while loop.
Latter code:
statement= gets.chomp
while statement != "BYE"
# if-else validation
statement= gets.chomp
end
Here you read user input, then start the while loop.:
Validate it against the while loop.
Validate it with your if-else statement.
Read user input.
Continue with loop.
The relevant part is when you decide to validate the user input to continue inside the while loop and what you do before or after that validation. Note that this doesn't belong to Ruby but to any programming language. The logic is which matters in this case.
In short, the order matters.
first end relates to the if/else, and the SECOND end refer to the "ending of the loop"?
Yes, this is entirely correct. This is easier to understand when you indent the code:
while statement != "BYE" # <----.
statement =gets.chomp # |
if statement != statement.upcase # <-. |
puts "WHAT?! SPEAK UP!" # | |
else # | |
year = rand(1940..1955).to_s # | |
puts "NO, NOT SINCE " + year # | |
end # <-. |
end # <----.
You could also have the loop in this form:
while true
statement = gets.chomp
# the statement below will be executed only if the condition on the right is met
break if statement == 'BYE'
# if-else condition
end
There are a number of issues with your script. The following is (in my opinion) easier to read and debug, and works in casual testing with Ruby 2.1.2:
puts "Talk to grandma!"
puts "To stop talking to Grandma say 'BYE'"
puts "Remember grandma has bad hearing. You might need to Yell!"
loop do
case gets.chomp
when 'BYE'
break
when /[[:lower:]]/
puts 'WHAT?! SPEAK UP!'
else
puts "NO, NOT SINCE #{rand 1940..1955}"
end
end
I am a beginner who is trying to learn Ruby. I have learned some of the easier stuff so far, but I seem to be stuck in trying to combine a couple of things I've learned.
What I am trying to do is to ask the user a question and tell them to enter either 1 or 2. A simple if statement would let me respond with one option if they enter 1, and another option if they enter 2. However, if they enter something entirely different like a different number, a string, etc., how can I prompt them to try again and have it loop back to the original question?
What I have so far looks something like this.
prompt = "> "
puts "Question asking for 1 or 2."
print prompt
user_input = gets.chomp.to_i
if user_input == 1
puts "One response."
elsif user_input == 2
puts "Second response."
else
puts "Please enter a 1 or a 2."
end
This is where I'm stuck. How do I make it go back to the "Question asking for 1 or 2." until the user enters a 1 or 2? I know it's probably a loop of some kind, but I can't seem to figure out which kind to use and how to incorporate asking for user input repeatedly (if necessary) until getting the desired input. Any help would be greatly appreciated. Thanks.
You're right that you need to run your code in a loop. Using a while loop with gets.chomp as a condition, you can carry on asking for user input until you decide you've got what you want.
In this case, you want to validate the answer to the question and ask again if it's invalid. You don't need to change a great deal, except making sure you break out of the loop when the answer is correct. If the answer is wrong, print the prompt again.
This is a slightly refactored version that uses case instead, but it shows what you need to do. There is no doubt a cleaner way to do this...
prompt = "> "
puts "Question asking for 1 or 2."
print prompt
while user_input = gets.chomp # loop while getting user input
case user_input
when "1"
puts "First response"
break # make sure to break so you don't ask again
when "2"
puts "Second response"
break # and again
else
puts "Please select either 1 or 2"
print prompt # print the prompt, so the user knows to re-enter input
end
end
Try using the until method like this:
prompt = "> "
print prompt
user_input = nil
until (user_input == 1 or user_input == 2)
puts "Please enter a 1 or 2."
user_input = gets.chomp.to_i
end
if user_input == 1
puts "One response."
elsif user_input == 2
puts "Second response."
else
puts "Please enter a 1 or a 2."
end
user_input = 0
until [1,2].include? user_input do
puts "Please enter a 1 or a 2.>"
user_input = gets.chomp.to_i
end
if user_input == 1
puts "One response."
else
puts "Second response."
end
You can try this to make your code clean.
While the title of this question is somewhat unrelated, please see the trick that is used here: Ruby Retry and ensure block is not working
The use of error detection and unique retry keyword available in Ruby allows you to easily do a retry-loop compacted together with nice an error handling.
However, mind that the example I pointed is not really the best. There are some minor issues. For example, you should not catch Exception, rather simple rescue => e would be enough. But the overall idea should be rather clear.
I'm curious if there's a way to have the program go back up the if statement stack?
Ideally, the program would return to line 2 and prompt the user for the input variable, then continue to evaluate like it did the first time. Think of it like a cursor in a text editor, I just want to move it from either of those two comments back up to line 2. The two places of interest are commented out below:
while true
input = gets.chomp
if input != input.upcase
puts "HUH?! SPEAK UP, SONNY!"
elsif input == 'BYE'
puts "HUH?! SPEAK UP, SONNY!"
input = gets.chomp
if input == 'BYE'
puts "HUH?! SPEAK UP, SONNY!"
input = gets.chomp
if input == 'BYE'
puts "GOOD BYE!";
break
else
# return to top-level if statement
end
else
# return to top-level if statement
end
else
random_year = rand(1930..1950)
puts "NO, NOT SINCE #{random_year}!"
end
end
In the code you show, you don't need to do anything to make the flow of execution go back to line 2. Just omit the else clauses in the two places you marked. The flow of execution will drop down to the bottom of the while loop, then loop back to the top, then go back to line 2.
You need to use a while statement to set a condition flag and check it, which will loop back to the while statement if you don't change the flag:
flag = 0
while flag1 == 0
if var = "string"
then ...statements...
flag1 = 1 ; this allows us to break out of this while loop
else ...statements...
end
end
If flag1 is not 0 at the end of the while statement, the while statement will loop back. For two such conditions, you need to nest the while loops. You might have to re-order your statements to make multiple while loops work this way.
You can avoid this level of neasted ifs with:
byecount = 0
while byecount < 3
input = gets.chomp
if input == "BYE"
byecount += 1
next
else
byecount = 0
end
if input != input.upcase
puts "HUH?! SPEAK UP, SONNY!"
else
puts "NO, NOT SINCE #{rand(1930..1950)}!"
end
end
puts "GOOD BYE!"
Or you can write a catch..throw flow structure. (Really.. if you need to use it, something is wrong with your design)
catch :exitloop do
while ...
if ...
if ...
if ...
throw :exitloop
end
end
end
end
end
Here's how I'd write a similar exercise:
BYE = 'BYE'
HUH = "HUH?! SPEAK UP, SONNY!"
loop do
input = gets.chomp
if input != input.upcase
puts HUH
next
end
if input != BYE
random_year = rand(1930..1950)
puts "NO, NOT SINCE #{random_year}!"
next
end
puts HUH
input = gets.chomp
if input == BYE
puts HUH
input = gets.chomp
if input == BYE
puts "GOOD BYE!";
break
end
end
end
I used loop instead of while. Matz, the main man for Ruby, recommends loop. See "Is there a “do … while” loop in Ruby?" for further discussion about it.