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

Posted by Guillaume Nargeot (Guest)
on 23.02.2007 02:55
(Received via mailing list)
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 ?
Posted by WATANABE Hirofumi (Guest)
on 23.02.2007 03:14
(Received via mailing list)
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
Posted by Kjell Olsen (Guest)
on 23.02.2007 03:24
(Received via mailing list)
Check this out for making your case statements work:

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

"Consider Olympic ice dancing out-graced!"

-kjell
Posted by Chad Perrin (Guest)
on 23.02.2007 04:23
(Received via mailing list)
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.
Posted by Andreas S (Guest)
on 23.02.2007 04:57
(Received via mailing list)
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
Posted by unknown (Guest)
on 23.02.2007 05:23
(Received via mailing list)
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 :-)

>      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
Posted by Florian Frank (Guest)
on 23.02.2007 05:42
(Received via mailing list)
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.
Posted by Guillaume Nargeot (Guest)
on 23.02.2007 06:05
(Received via mailing list)
> 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 ;)
Posted by Brian Candler (Guest)
on 23.02.2007 11:32
(Received via mailing list)
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"
Posted by Olivier Renaud (Guest)
on 23.02.2007 14:19
(Received via mailing list)
Le vendredi 23 février 2007 11:30, Brian Candler 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
Posted by Brian Candler (Guest)
on 23.02.2007 16:27
(Received via mailing list)
On Fri, Feb 23, 2007 at 10:18:34PM +0900, Olivier Renaud 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.
Posted by gga (Guest)
on 23.02.2007 19:20
(Received via mailing list)
On 22 Feb., 22:54, Guillaume Nargeot
<guillaume.nargeotDONOTFUCKINGS...@fusionsystems.co.jp> 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.
Posted by Giles Bowkett (Guest)
on 26.02.2007 08:43
(Received via mailing list)
> 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.
Posted by Pit Capitain (Guest)
on 28.02.2007 20:36
(Received via mailing list)
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
Posted by MenTaLguY (Guest)
on 28.02.2007 20:55
(Received via mailing list)
On Thu, 1 Mar 2007 04:36:39 +0900, Pit Capitain <pit@capitain.de> 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
Posted by Pit Capitain (Guest)
on 28.02.2007 21:02
(Received via mailing list)
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
Posted by Brian Mitchell (Guest)
on 28.02.2007 21:06
(Received via mailing list)
On 2/28/07, Pit Capitain <pit@capitain.de> 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.
Posted by Brian Candler (Guest)
on 28.02.2007 21:15
(Received via mailing list)
On Thu, Mar 01, 2007 at 04:36:39AM +0900, Pit Capitain 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.