How to "break" out of a case...while in Ruby - ruby

So, I've tried break, next and return. They all give errors, exit of course works, but that completely exits. So, how would one end a case...when "too soon?"
Example:
case x
when y; begin
<code here>
< ** terminate somehow ** > if something
<more code>
end
end
(The above is some form of pseudo-code just to give the general idea of what I'm asking [begin...end was used with the hope that break would work].
And, while I'm at it, is there a more elegant way of passing blocks to case...when?

What's wrong with:
case x
when y;
<code here>
if !something
<more code>
end
end
Note that if !something is the same as unless something

I see a couple of possible solutions.
At the first hand, you can define your block of instructions inside some method:
def test_method
<code here>
return if something
<more code>
end
case x
when y
test_method
end
At the other hand, you can use catch-throw, but I believe it's more uglier and non-ruby way :)
catch :exit do
case x
when y
begin
<code here>
throw :exit if something
<more code>
end
end
end

Ruby has no built-in way to exit the "when" in a "case" statement. You could however get the desired outcome with an answer similar to the technique WarHog gave:
case x
when y
begin
<code here>
break if something
<more code>
end while false
end
or if you prefer:
case x
when y
1.times do
<code here>
break if something
<more code>
end
end

Related

How to convert this if condition to unless?

if array.present?
puts "hello"
end
There is no else part to this.
How to write the above if condition using unless.
I'm asking this question because of this lint error:
Use a guard clause instead of wrapping the code inside a conditional expression
Regarding your comment:
I'm asking this question because of this lint error
Use a guard clause instead of wrapping the code inside a conditional expression
This means that instead of:
def foo(array)
if array.present?
puts "hello"
end
end
You are supposed to use:
def foo(array)
return unless array.present?
puts "hello"
end
See https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals
If this is a Rails question (is it?), you can also use blank?:
def foo(array)
return if array.blank?
puts "hello"
end
There's no reason to.
Remember: unless is the inverse of if (or !if if you rather), and is only intended to make your code easier to read.
Using unless with your expression would be incredibly awkward, because you're now moving the actual body of work to an else statement...
unless array.present?
return
else
puts "hello"
end
...which doesn't make your code any easier to read if you had stuck with a negated if:
if !array.present?
return
else
puts "hello"
end
Don't use unless here. You lose readability in exchange for virtually nothing.
One-liner:
puts "hello" unless !array.present?
However, I would recommend:
puts "hello" if array.present?
unless array.present?
return
else
puts "hello"
end
OP requested one-liner modification:
Pseudocode:
something unless condition
Therefore:
puts "hello" unless !array.present?

Repeating a block of code until the block itself returns false?

I want to:
pass a block to a method call, and then
pass that entire method call as the condition of a while loop,
even though I don't need to put any logic inside the loop itself.
Specifically, I have an array that I'd like to #reject! certain elements from based on rather complicated logic. Subsequent calls to #reject! may remove elements that were not removed on a previous pass. When #reject! finally stops finding elements to reject, it will return nil. At this point, I would like the loop to stop and the program to proceed.
I thought I could do the following:
while array.reject! do |element|
...
end
end
I haven't actually tried it yet, but this construction throws vim's ruby syntax highlighter for a loop (i.e., it thinks the first do is for the while statement, and thinks the second end is actually the end of the encapsulating method). I also tried rewriting this as an inline while modifier attached to a begin...end block,
begin; end while array.reject! do |element|
...
end
but it still screws up the highlighting in the same way. In any case, it feels like an abuse of the while loop.
The only way I could think of to accomplish this is by assigning the method call as a proc:
proc = Proc.new do
array.reject! do |element|
...
end
end
while proc.call do; end
which works but feels kludgy, especially with the trailing do; end.
Is there any elegant way to accomplish this??
It's not just vim, while array.reject! do |element| is invalid syntax:
$ ruby -c -e 'while array.reject! do |element| end'
-e:1: syntax error, unexpected '|'
while array.reject! do |element| end
^
You could use { ... } instead of do ... end:
while array.reject! { |element|
# ...
}
end
or loop and break:
loop do
break unless array.reject! do |element|
# ...
end
end
a little more explicit:
loop do
r = array.reject! do |element|
# ...
end
break unless r
end
Ruby lets you move your condition to the end of the loop statement. This makes it easy to store a result inside of the loop and check it against the conditional:
begin
any_rejected = arr.reject! { … }
end while any_rejected
This would work the same as doing end while arr.reject! { … }, but it's much clearer here what's happening, especially with a complicated reject!.
You're right that the Ruby parser thinks that do belongs to while, and doesn't understand where the second end is coming from. It's a precedence problem.
This code is just to show that it can be done. For how it should be done, see Stefan's answer :
array = (1..1000).to_a
while (array.reject! do |element|
rand < 0.5
end)
p array.size
end
It outputs :
473
238
113
47
30
18
8
1
0
My personal preference in situations where I need to call a method until the return value is what I want is:
:keep_going while my_method
Or more tersely I sometimes use:
:go while my_method
It's one line, and you can use the contents of the symbol to help document what's going on. With your block, I'd personally create a proc/lambda out of it and pass that to reject for clarity.
# Harder to follow, IMHO
:keep_going while array.reject! do |...|
more_code
end
# Easier to follow, IMHO
simplify = ->(...){ ... }
:keep_simplifying while array.reject!(&simplify)

Writing pretty & consistent multiline-conditions?

For a better readability i normally prefer writing multiline-conditions in if-statements as follows:
if
something == nice and
anotherthing == bad
then
do_something
and_so_on
end
I am currently facing while-loops (at a point where i can't avoid them) and have to use multiple conditions. I tried the following and got a corresponding syntax error, unexpected keyword_do_block (SyntaxError):
while
something == nice and
anotherthing == bad
do
do_something
and_so_on
end
It turned out that the optional do-keyword can't stand alone in the next line, in opposite to the then-keyword. This is how it works -- after putting the do at the end of the preceding line:
while
something == nice and
anotherthing == bad do
do_something
and_so_on
end
For my comprehension of readability, this is probably not as readable as the corresponding if-statement, which "enforces" a newline and accentuates the indentation (again).
Did i miss an alternative syntax and/or is it probably a flaw in Ruby's syntax-design (shouldn't it be possible to put the do into the next line)?
You can use begin end while syntax, Your conditions will be at the end of the statement but as the if statement:
begin
do_something
and_so_on
end while
something == nice and
anotherthing == bad
BUT in this case it will execute once before checking the condition
You can kind of reverse it and it will work:
while
something == nice and
anotherthing == bad
begin
do_something
and_so_on
end
end
Good luck!

Is there a Way To Exit Two Loops by Single Command In Ruby?

Here is the Sample code,
while true
while true
exit all loops when condition true
end
end
Can someone tell me if is it possible here to exit first loop when second loop breaks, but then I want to use only one break command and no raise.
You know what's better than using only one break? Not using any at all! :)
Little-used throw/catch is good here
catch(:done) do
while cond1
while cond2
throw :done if condition
end
end
end
For more information, see the docs on throw and catch.
Alright, so apparently boolean flags are a no-go. Oops.
The other thing that pops to mind is catching an error, but you said you don't want to do that, or wrap it in a method and return. Honestly, there doesn't seem to be a way, but here's the simplest I could come up with:
catch (:exit) do
while true
while true
throw :exit if condition
end
end
end
You could also throw an exception, but that seems dirty. Here's the code to do it, though:
begin
while true
while true
raise "Exiting loops" if condition
end
end
rescue
#Code to go after the loop
end
Lastly, you could wrap the whole thing in a method and return from that method:
def my_descriptive_method_name()
while true
while true
return if condition
end
end
end

Using code block in ruby's if statement

is there a way to pass a code block as a ruby if condition?
I have a list of regular expressions and I want to check if a message matches any of them and then do something accordingly or else do something different.
Here is an example snippet for how I think it should be written:
msg_values.each do |msg|
if (SKIP_MSG_ARRAY.each { |regular_exp| return true if msg.match(regular_exp)})
# do something
else
# do something else
end
end
is it possible? or else what is the best way of writing something like this?
Use Enumerable#any?. See the reference docs: http://ruby-doc.org/core-2.0/Enumerable.html. This method (as well as Enumerable#all?) will return true or false.
Example:
msg_values.each do |msg|
if (SKIP_MSG_ARRAY.any? { |regular_exp| msg.match(regular_exp)})
# do something
else
# do something else
end
end

Resources