Ruby's "case" doesn't behave like a normal switch

The problem with ruby is that you can’t use a switch as it behaves with
many
other languages like C, php, etc.

For example, you want to convert the following peace of code to ruby:

switch (x) {
case 1:
case 2:
# do something
break;
case 3:
# do some other thing
break;
default:
}

it seems you can do it with the following code:

case x
when 1…2
# do something
when 3
# do some other thing
else
end

But when it comes to strings, you would like to be able to do simply:

case x
when “this”
when “that”
# do something common to “this” and “that”
when “nothing”
# something different
end

The problem is that you can’t, because of the fact the “break” is
implicit
in
the ruby language.
The fact is that you don’t want to rewrite the same code two times.
Of course, you would be able to right your code into a function, and
then
just
write:

case x
when “this”
func_this_that()
when “that”
func_this_that()
when “nothing”
# something different
end

But if again, you don’t want to relay on a function call, here’s a
solution
I thought about.

This is not perfect, because you have to change your syntax, but it
might be
quite useful in some cases.

Redefine the === method of the Array class, that is used to match the

argument

of the “case” directive.

class Array

Return true if self contains any element of the given parameter.

def ===(o)
o.each do |e|
return true if self.include?(e)
end
false
end
end

Example 1

x = [“case4”]

case x
when [“case1”, “case2”]
puts “first case”
when [“case3”, “case4”, “case5”]
puts “second case”
when [“case6”]
puts “third case”
else
puts “not matched”
end

Returns: “second case”

Example 2

x = [6]

case x
when [1, 2]
puts “first case”
when [3, 4, 5]
puts “second case”
when [6]
puts “third case”
else
puts “not matched”
end

Returns: “third case”

What do you think about it ?

Hi,

On Fri, Feb 23, 2007 at 10:55:06AM +0900,
Guillaume Nargeot wrote:

when “nothing”
# something different
end

case x
when “this”, “that”
func_this_that()
when “nothing”
# something different
end

Check this out for making your case statements work:

http://redhanded.hobix.com/bits/wonderOfTheWhenBeFlat.html

“Consider Olympic ice dancing out-graced!”

-kjell

On Fri, Feb 23, 2007 at 10:55:06AM +0900, Guillaume Nargeot wrote:

But when it comes to strings, you would like to be able to do simply:

case x
when “this”
when “that”
# do something common to “this” and “that”
when “nothing”
# something different
end

These two examples are not equivalent. In one, you’re listing multiple
cases on a single line with the associated code following it. In the
other, you’re listing cases separately and letting the lack of a break
cause it to fail over to the next – which leads to a very unintuitive
set of code. Why not just employ your list of cases for the same
resulting code on one line? That works just fine.

This is a long shot, but since there are many EE guys here… does
anybody
know if there’s any effort going on for openaccess database ruby
binding?

I want to try it myself, but it looks like a big task, so I thought I
ask
first

-andre

Hi –

On Fri, 23 Feb 2007, Guillaume Nargeot wrote:

The problem with ruby is that you can’t use a switch as it behaves with many
other languages like C, php, etc.

I don’t consider that a problem. If all languages had to behave
exactly the same way, there would only be one language, and we’d all
get bored :slight_smile:

 return true if self.include?(e)

puts “first case”

Example 2

puts “not matched”
end

Returns: “third case”

What do you think about it ?

It’s very dangerous. Remember that changing Array#=== changes it
everywhere. There may be places in Ruby itself, or in other libraries
you’re using, that depend on the original behavior.

David

On Fri, Feb 23, 2007 at 02:05:10PM +0900, Guillaume Nargeot wrote:

case x
when “this”, “that”
func_this_that()
when “nothing”
# something different
end

Thank you very much !!!
I thought I tested everything, but obviously not this one.
I didn’t even find anything about it in the official documentation.

http://www.rubycentral.com/book/tut_expressions.html
Search down to the section headed “Case Expressions”

case x
when “this”, “that”
func_this_that()
when “nothing”
# something different
end

Thank you very much !!!
I thought I tested everything, but obviously not this one.
I didn’t even find anything about it in the official documentation.

So, thank you again, that is exactely what I needed :wink:

Guillaume Nargeot wrote:

else
puts “first case”
What do you think about it ?

You can already do this:

a = [3, 4, 5]
case x
when 1, 2
puts “first case”
when *a
puts “second case”
when 6
puts “third case”
else
puts “not matched”
end

There are cases of C’s fall-through switch, that cannot be emulated by
Ruby’s case - and I think that’s a good thing. To make breaks necessary
to break out of a case clause (and creating the danger for programmers
to incidentally forget one), was probably one of the most inane ideas of
the C language’s creators.

Le vendredi 23 février 2007 11:30, Brian C. a écrit :

I didn’t even find anything about it in the official documentation.

http://www.rubycentral.com/book/tut_expressions.html
Search down to the section headed “Case Expressions”

I didn’t know about this syntax, either. Thanks, Eban ! For the Strings,
I
thought about using Regex, instead :

case x
when /this|that/
func_this_that()
when “nothing”
# something different
end

On 22 Feb., 22:54, Guillaume Nargeot
[email protected] wrote:

case 3:
# do some other thing
break;
default:

}

For completeness sake, you can also emulate C’s lack of a break;
statement in ruby by doing redo/retry, like:

a = 1

catch(:redo) do
case a
when 1
print ‘1’
a = 2
redo
when 2
puts ‘and 2’
end
end

This makes ruby’s case statement probably the best of any language.

There are cases of C’s fall-through switch, that cannot be emulated by
Ruby’s case - and I think that’s a good thing. To make breaks necessary
to break out of a case clause (and creating the danger for programmers
to incidentally forget one), was probably one of the most inane ideas of
the C language’s creators.

Yeah – this is a feature which you might not recognize as such
because you’re used to the bug.

gga schrieb:

redo

when 2
puts ‘and 2’
end
end

This makes ruby’s case statement probably the best of any language.

I didn’t know the “catch(:redo)”. Is this documented somewhere?

Regards,
Pit

On Thu, 1 Mar 2007 04:36:39 +0900, Pit C. [email protected] wrote:

catch(:redo) do
case a
when 1
print ‘1’
a = 2
redo
when 2
puts ‘and 2’
end
end

I didn’t know the “catch(:redo)”. Is this documented somewhere?

It doesn’t have to be catch(:redo) specifically, and in fact its use may
be misleading – here, catch is simply being used to introduce an
iteration context. For example, this works just as well:

def foo ; yield ; end

a = 1
foo do
case a
when 1
print ‘1’
a = 2
redo
when 2
puts ‘and 2’
end
end

-mental

MenTaLguY schrieb:

It doesn’t have to be catch(:redo) specifically, and in fact its use
may be misleading – here, catch is simply being used to introduce an
iteration context.
(…)

Ah, of course. Thanks Mental.

Regards,
Pit

On Fri, Feb 23, 2007 at 10:18:34PM +0900, Olivier R. wrote:

I thought I tested everything, but obviously not this one.
func_this_that()
when “nothing”

# something different

end

That’s not the same though. I think you’d need:

when /\A(this|that)\z/

(which is a gotcha if you come from Perl, and expect to use ^…$ to
match
the start and end of the string)

Regards,

Brian.

On 2/28/07, Pit C. [email protected] wrote:

a = 2
redo

when 2
puts ‘and 2’
end
end

This makes ruby’s case statement probably the best of any language.

I didn’t know the “catch(:redo)”. Is this documented somewhere?

Well the redo is really reacting as part of the catch block (though
other block forms would do). Consider this example:

def my_iter(exp)
yield
end

my_iterr(p(:arg)) {p :block}
#=> :arg and :ok once in order

Caution with the following. have ^C handy

my_iter(p(:arg)) {p :before; redo; p :after}
#=> :arg once and then infinite loop of :before

my_iter(p(:arg)) {p :before; retry; p :after}
#=> :arg then :before. both repeated infinitely

Brian.

The switch is a bad pattern and better never use it.
When it comes to use it though, just use that “simple” version of ruby,
it’s the best you can offer yourself.

Improve the interoperability of your code by using better patterns and
if you still want to stick the the “switch” kind of dispatching, try a
hash that will match your key to a function to be executed.
That way you can have heterogeneous keys… the equivalent of :
case x
when 1
do_1
when ‘two’
do_2
when [:a, :b]
do_a_b

Just create your “hash” data :
my_redirection = {
1 => do_1,
‘two’ => do_2,
[:a, :b] => do_a_b

}
then use my_redirection[x].call
Each element do_* can be a function, a proc, a lambda.
And it is absolutely natural to have do_2 calling do_1 :

def do_2
do_1
… specific of do_2
end

You can also define a class to “redirect” functionality and not use
directly a Hash structure.

And for sure, you can go for better patterns since ruby is full object.

It’s better not to use that “switch” structure in ruby… you will
really never have to use it.

Have fun ^_^~

On Thu, Mar 01, 2007 at 04:36:39AM +0900, Pit C. wrote:

a = 2
redo
when 2
puts ‘and 2’
end
end

This makes ruby’s case statement probably the best of any language.

I didn’t know the “catch(:redo)”. Is this documented somewhere?

‘redo’ is documented at
http://www.rubycentral.com/book/tut_expressions.html

‘catch’ is documented at
http://www.rubycentral.com/book/tut_exceptions.html

However they’re not a pair, and I don’t think that ‘catch’ does anything
in
particular in the above example, apart from causing the block to be
executed
once. You could achieve the same with:

a = 1
1.times do
case a
when 1
print ‘1’
a = 2
redo
when 2
puts ‘and 2’
end
end

Or:

a = 1
begin
case a
when 1
print ‘1’
a = 2
redo
when 2
puts ‘and 2’
end
end while false

Regards,

Brian.

You can also use a Regex in a case/when structure:

i = ‘foo’

case i
when /fo/
puts ‘Yeah, fo is included in our String.’
end