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

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.

Related

Use ruby to go back to early lines and functions

I am making a basic hangman game that will need to keep retracing until the game is over. I'm trying to figure out the correct ruby way to go back to these lines. Here is a rough pseudocode example:
some_function
end
another_function
end
if x > 5 go back to some_function
if x < 5 go back to another_function
Functions are how you run some code somewhere else and return back to where you started. Loops are how you run the same code again and again until some condition is met.
Here is some incomplete code to demonstrate.
def some_function(x)
puts "x is #{x} some function"
end
def another_function(x)
puts "x is #{x} in another function"
end
def something_else(x)
puts "x is #{x} in something else"
end
# I've left are_we_done undefined, it's the condition for
# stopping the loop. I've also left get_x undefined.
while !are_we_done
x = get_x
if x > 5
some_function(x)
elsif x < 5
another_function(x)
else # x is 5
something_else(x)
end
Teaching these fundamentals is beyond a Stack Overflow answer. I would suggest going through a tutorial like Learn Ruby The Hard Way to learn the fundamentals of loops and functions.

Where does the extra "1" in this output come from?

I have a simple code that produces a times table:
def times_table(rows)
columns = rows
1.upto(rows) do |x|
1.upto(columns) do |y|
printf "%5d", x*y
end
print "\n"
end
end
When I try to place this in a file "codetester.rb" followed by the line puts times_table(4), I get the following result:
$ ruby codetester.rb
1 2 3 4
2 4 6 8
3 6 9 12
4 8 12 16
1
Where is the extra '1' at the end coming from, and (cute as it is) how can I avoid it?
To fix this without changing your method body, don't call puts and only use:
times_table(4)
You're getting the extra 1 because you wrote:
puts times_table(4)
This says, "print the value of times_table(4)". The value of this is "1", because the return value of times_table(4) is equal to:
1.upto(rows) { ... }
and upto returns the value of what it was called on.
Here's a simpler, analogous example, since OP seems a little confused about how return values work.
Let's say you had this method:
def foo
puts "hello!"
100
end
If you do this:
foo
then "hello!" will be printed as a side effect of calling foo. The return value of 100 is not printed, because you didn't print the value of foo itself. So you will see:
hello!
as the output.
By contrast, if you do this:
puts foo
then "hello!" will be printed as a side effect of calling foo, as before. But this time, the return value is also printed, because you wrote puts foo. So you will see:
hello!
100
as the output.

Is there a limit to the number of `and`s in Ruby statement?

The code below works as I want, switching a light on or off with each keypress of l. However, when I try to add other things I want done with the switching, I can't.
look = 0
lighton = 0
while look < 10
system 'stty cbreak'
q = $stdin.sysread 1
case q ### #{q}
when "l" then if lighton==1 then lighton=0 and puts "ight off"
else lighton=1 and puts "ight on" end
end
system 'stty cooked'
look += 1
end #while
If I add another and it isn't seen but I get no error:
look = 0
lighton = 0
while look <10
system 'stty cbreak'
q = $stdin.sysread 1
case q ### #{q}
when "l" then if lighton==1 then lighton=0 and puts "ight off" and puts "light still off"
else lighton=1 and puts "ight on" end
end
system 'stty cooked'
look += 1
end #while
I'd like to add several more statements to both the if and else portions but can't.
There's no limit, you're just misusing the and operator. It's not meant to do "this and that", it does "do this and, if it is truthy, do this too". Here's a quick example:
1 and puts 'falsy'
nil and puts 'truthy'
# prints: falsy
Since puts returns nil, which is falsy, puts 'hello' and puts 'world' will only print "hello".
So, don't use and to create a one-liner. You could use ; instead, but that doesn't help readability. Instead just use multiple lines! It's much clearer what's going on:
case q
when "l"
if lighton == 1
lighton = 0
puts "light off"
puts "light still off"
else
lighton = 1
puts "light on"
end
end
You may wish to read more about and/or in Ruby and how they differ from &&/||.
I know nothing about ruby but as there are multiple thing happening after the then in your if statment, do you need to group them somehow? ie Java you would stick them in {}

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

Why doesn't "case" with "when > 2" work?

Why is this not working?
case ARGV.length
when 0
abort "Error 1"
when > 2
abort "Error 2"
end
It's not valid ruby syntax.
What you need is
case
when ARGV.length == 0
abort "Error 1"
when ARGV.length > 2
abort "Error 2"
end
When you write case x, the important part you need to understand is that ruby takes the x and then applies a comparison to the argument or expressions you insert in the when clause.
The line where you say when x >2 reads to ruby like:
if ARGV.length == > 2
When you remove a specific object from the case statements, you can apply conditionals within the when statements .
Use 1.0 / 0.0 to get infinity, which fixes #mosch's code:
case ARGV.length
when 0
raise "Too few"
when 3..(1.0/0.0)
raise "Too many"
end
You don't have to be Chuck Norris to divide by a floating point zero.
Well, it doesn't work because it's not valid ruby syntax. However, you can do this:
x = 15
case x
when 0..9 then puts "good"
when 10..12 then puts "better"
when 13..200 then puts "best"
else
puts "either great or poor"
end
An if statement would probably be more fitting for your code, since you don't have a definitive range/value, but rather just a greater-than:
if ARGV.length == 0
abort "Error 1"
elsif ARGV.length > 2
abort "Error 2"
end
You can use either if elsif or lambda/proc (then notation is just for shortness):
case x
when 1 then '1'
when ->(a) { a > 2 } then '>2'
end
You can also create a named variable for clarity:
greater_than_2 = ->(a){ a > 2 }
case x
when 1 then '1'
when greater_than_2 then '>2'
end
You can also create your own comparison predicate:
greater_than = ->(n) { ->(a) {a > n} }
case x
when 1 then '1'
when greater_than[2] then '>2'
when greater_than[5] then '>5'
end
Answering your initial question when > 1 is not a valid syntax, since > is binary operator in Ruby, which is to say its arity 2, meaning it takes 2 arguments. Don't be confused of term binary vs bitwise.

Resources