How to write a case statement in Ruby with multiple statements inside a when clause? - ruby

I tried separating the statements inside the when clauses with commas but it didn't work.
when 1; statement, statement, statement
when 2; statement, statement, statement
I couldn't find any examples of this online.
case selection
when 1
system "clear"
view_all_entries
main_menu
when 2
system "clear"
create_entry
main_menu
when 3
system "clear"
search_entries
main_menu
when 5
puts "Good bye!"
exit(0)
else
system "clear"
puts "Sorry, that is not a valid input"
main_menu
end

Your question seems to be "how can I put all those statements on one line to have fewer lines".
Generally, you can use ";" in ruby to replace the End-of-Lines. Thus:
case selection
when 1; system "clear"; view_all_entries; main_menu
...
Or
case selection
when 1 then system "clear"; view_all_entries; main_menu
...
Using the ";" in any way is very much not ruby-like and not recommended. See below for a much nicer refactoring which strips the duplicated code.

I would be inclined to write it thusly:
if selection==5
puts "Good bye!"
exit(0)
end
system "clear"
case selection
when 1 then view_all_entries
when 2 then create_entry
when 3 then search_entries
else puts "Sorry, that is not a valid input"
end
main_menu

i've tried comma, it does not work
Yes, it does:
case value
when 1, 2
"one or two"
else
"something other than one or two"
end
returns "one or two" if value is 1 or 2

You can use the when - then syntax. Note that it is considered bad practice if the lines become too long:
case value
when 1 then statement1
when 2 then statement2
when 3 then statement3
when 4 then statement4
end
To execute multiple statements for the same condition on the same line you can separate them with ;. However, this is always considered a bad practice:
case value
when 1 then statement1; statement11; statement12
when 2 then statement2; statement21; statement22
when 3 then statement3; statement31; statement32
when 4 then statement4; statement41; statement42
end

Related

How to understand end tag/loop relations in Ruby?

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

Looping through a case statement in Ruby?

I am trying to determine the best way to loop through a case statement until a user provides a certain input (in this case, exit).
So far, my code works with a while loop, but it seems a little redundant when I have input = gets.chomp over and over.
Here's a bit of abbreviated code:
input = gets.chomp
while input.downcase != 'exit'
case input.downcase
when 'help'
puts "Available commands are..."
input = gets.chomp
#more when statements go here...
else
puts "That is not a valid command. Type 'HELP' for available commands."
input = gets.chomp
end
end
puts "Goodbye!"
I'd write it like:
loop do
input = gets.chomp.downcase
case input
when 'help'
puts "Available commands are..."
# more when statements go here...
when 'exit'
break
else
puts "That is not a valid command. Type 'HELP' for available commands."
end
end
puts "Goodbye!"
A loop is designed for this sort of case, where you just want to loop cleanly, and then eventually break out on some condition.
For ultimate clarity in what the code is doing, I'd put the exit immediately after reading the input, instead of being embedded in the case statements. It's a minor thing, but is useful to remember if you're coding and others have to help maintain it:
loop do
input = gets.chomp.downcase
break if input == 'exit'
case input
when 'help'
puts "Available commands are..."
# more when statements go here...
else
puts "That is not a valid command. Type 'HELP' for available commands."
end
end
puts "Goodbye!"
Why don't you change the while to:
while (input = gets.chomp.downcase) != 'exit'
Note that this also means that instead of using case input.downcase, you can use case input, as it has already been made lowercase.
edit: my roots in C betray me...
As mentioned in the comments, this is not a particularly "ruby-esque" solution. It also causes a stack trace when gets returns nil. You might prefer to split it into two lines:
while (input = gets)
input = input.chomp.downcase
break if input == 'exit'
case input
# when statements
else
puts "That is not a valid command. Type 'HELP' for available commands."
end
end
I separated the "exit" from the case criteria for a couple of reasons:
It was part of the loop logic in the question, so it's (arguably) more readable to keep it separate from the other cases.
I didn't realise that break behaves differently in Ruby case statements to other languages that I am more familiar with, so I didn't think it would do what you wanted.
You could equally well do this:
while (input = gets)
case input.chomp.downcase
when 'exit'
break
# other when statements
else
puts "That is not a valid command. Type 'HELP' for available commands."
end
end
edit: I am delighted to hear that like Perl, Ruby also has the $_ variable, to which the value of gets will be assigned:
while gets
case $_.chomp.downcase
when 'exit'
break
# other when statements
else
puts "That is not a valid command. Type 'HELP' for available commands."
end
end
You can even get rid of input by exiting the loop with break instead of checking the result in the while condition
while true
case gets.chomp.downcase
when 'exit'
break
when 'help'
# stuff
else
# other stuff
end
end

Re-open a script in ruby

I would like to re-open my script at the end of its execution. I tried with load but it didn't work. Is what I want to do possible? If yes, what should I try?
This is the structure of my script:
usr_choice = gets.chomp.to_i
case usr_choice
when 1
# Do something
when 2
# Do something else
else
puts "Choice not recognised"
end
Basically I would like for example to go back to the user input (usr_choice) after going through the case statement. Without a GoTo.
So you want a loop? Well, use loop:
loop do
usr_choice = gets.chomp.to_i
case usr_choice
when 1
puts 'one'
when 2
puts 'two'
else
break # exit the loop (not the case construct as in other languages)
end
end
Example:
$ ruby filename.rb
1
one
2
two
1
one
kthxbye
$

Ruby remove the implicit break in Case statement? (How to make case like Switch)

x='bob'
case x
when "bob"
puts 'it stops here'
when 'bob'
puts 'but i want it to stop here'
end
Is there anyway to make case statements behave like the vanilla switch? So that it'll cycle through all the "when's" before breaking out? I'm surprised that ruby has it behave almost identically like a elsif.
Michael,
While your example is a bit misleading ('bob' matches both 'bob' and "bob" so the first case would always match), you just can use simple if's like in if_test method below :
def case_test(x)
puts case
when x > 3
"ct: #{x} is over 3"
when x > 4
"ct: #{x} is over 4"
end
end
case_test(4)
case_test(5)
def if_test(x)
puts "it: #{x} is over 3" if x > 3
puts "it: #{x} is over 4" if x > 4
end
if_test(4)
if_test(5)
This yields :
ct: 4 is over 3
ct: 5 is over 3
it: 4 is over 3
it: 5 is over 3
it: 5 is over 4
Note that you can also use multiple statements with when, which might help you or not depending on your real use case :
def many(x)
case x
when 'alice','bob'
puts "I know #{x}"
elseĀ·
puts "I don't know #{x}"
end
end
many('alice')
many('bob')
many('eve')
Yields :
I know alice
I know bob
I don't know eve
No. Case statements evaluate the first when block whose target's === method evaluates to true when passed the comparison, and stop there.

ruby - How to return from inside eval?

I have a code which I need to use within eval. Sometimes I need to get out from the eval code, but my tries lead to errors.
E.g.:
# expected to see 1, 2 and 5; not 3 nor 4; and no errors
eval "puts 1; puts 2; return; puts 3; puts 4" # => Error: unexpected return
puts 5
I tried with return, end, exit, break, and I couldn't get success. exit doesn't raise errors, but then I don't get the 5.
(Note: I know that eval is evil, but in this case I need to use it.)
Thank you all, but I found a solution which fits best into my problem:
lambda do
eval "puts 1; puts 2; return; puts 3; puts 4"
end.call
puts 5
This way the intuitive return keyword can be used inside eval to get out from it successfully.
I didn't like the conditional-like solutions in this case because it would force me (or the user) to add an end at the end.
About using throw/catch or break, I consider the return keyword more intuitive.
eval'd code is just being run in this place. It's not a function or block. How would you do it without eval? Probably like this:
puts 1
puts 2
if(conditionFor3And4)
puts 3
puts 4
end
you Can't. You can return out of methods, and break out of blocks or loops, but not eval.
You could try a throw/catch block instead
eval "
should_stop = true
catch :stop do
puts 1
puts 2
throw :stop if should_stop
puts 3
end
"
or this:
should_stop = true
catch :stop do
eval "
puts 1
puts 2
throw :stop if should_stop
puts 3
"
end
or just do a conditional like Mchl said, since you probably want it to conditional stop, not just always, but throw catch will let you jump out of a block no matter how many levels down you are, which make it more robust, if you need to break out of a nested loop or something like that.
You could just use conditionals instead of early returns.
You could quite happily define a function and execute it last at the end of the script as follows:
def ev(s)
eval("def doStuff()\n" + s + "\nend\ndoStuff()")
end
ev("
1
return 2
3
")
#=> 2
Demo

Resources