Forum: Ruby fun with "case"

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2007-06-10 12:35
(Received via mailing list)
A funny (and readable) way to test collection sizes just occurred to me:

irb(main):001:0> class Integer
irb(main):002:1>   def elements
irb(main):003:2>     cond = lambda {|enum| self == enum.size}
irb(main):004:2>     class <<cond
irb(main):005:3>       alias :=== :call
irb(main):006:3>     end
irb(main):007:2>     cond
irb(main):008:2>   end
irb(main):009:1> end
=> nil
irb(main):010:0> case [1,2,3]
irb(main):011:1> when 3.elements
irb(main):012:1>   puts "three!"
irb(main):013:1> when 5.elements
irb(main):014:1>   puts "too much!"
irb(main):015:1> else
irb(main):016:1*   puts "else"
irb(main):017:1> end
three!
=> nil

:-)

Kind regards

  robert
474216d84e3b5381cba84cd04a3a3b35?d=identicon&s=25 Joachim Glauche (joaz)
on 2007-06-10 12:49
Why not do a simply use

case [1,2,3].size

instead?
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2007-06-10 13:21
(Received via mailing list)
On 10.06.2007 12:49, Joachim Glauche wrote:
> Why not do a simply use
>
> case [1,2,3].size
>
> instead?

Because a) it's too simple and bloody obvious, b) less fun (see subject
:-)) and c) it does not work if you also have other criteria (i.e. which
do not use the size but content). :-)

Kind regards

  robert
C06869c119472a139eb163b72040b0db?d=identicon&s=25 Bertram Scharpf (Guest)
on 2007-06-10 13:33
(Received via mailing list)
Hi,

Am Sonntag, 10. Jun 2007, 19:49:43 +0900 schrieb Joachim Glauche:
> Why not do a simply use
>
> case [1,2,3].size
>
> instead?

Maybe you want to test for other properties.

Besides that it's a lot of fun finding out what one can do
in a sohisticated programming language.

Therefore.

Bertram
19605c7ede229e507e894865308a6d53?d=identicon&s=25 Gustav Paul (Guest)
on 2007-06-10 13:45
(Received via mailing list)
Robert Klemme wrote:
> irb(main):008:2>   end
> three!
> => nil
>
> :-)
>
> Kind regards
>
>     robert
>
>
lol, that's pretty sweet :-)

G
703fbc991fd63e0e1db54dca9ea31b53?d=identicon&s=25 Robert Dober (Guest)
on 2007-06-10 13:51
(Received via mailing list)
On 6/10/07, Robert Klemme <shortcutter@googlemail.com> wrote:
> On 10.06.2007 12:49, Joachim Glauche wrote:
> > Why not do a simply use
> >
> > case [1,2,3].size
> >
> > instead?
>
> Because a) it's too simple and bloody obvious, b) less fun (see subject
> :-)) and c) it does not work if you also have other criteria (i.e. which
> do not use the size but content). :-)
or in other words you can write

case list
   when []
      puts :empty
   when 1
      puts :not_empty
   else
      puts :close_to_infinity
end

as a matter of fact writing

case list.size
becomes an unnecessary early commitment!!!

T'is really kool Robert

Cheers
Robert
703fbc991fd63e0e1db54dca9ea31b53?d=identicon&s=25 Robert Dober (Guest)
on 2007-06-10 14:06
(Received via mailing list)
On 6/10/07, Robert Klemme <shortcutter@googlemail.com> wrote:
> irb(main):008:2>   end
> three!
> => nil
>
> :-)
>
> Kind regards
>
>         robert
>
>
As I said, this is really cool, now here comes a first quick hack of
generalization, you gotta file an RCR for this ;)
Please note the absence of "@" in my code ;)


class Module
  def define_casey args={}
    arg_mth = args[:on]
    name = args[:name]
    trans = args[:transform]
    define_method name do
      cond = lambda{ |x|
  trans ? self.send(trans) == x.send( arg_mth ) :
    self == x.send( arg_mth )
      }
      class << cond
  alias_method :===, :call
      end
      cond
    end
  end
end

class Integer
  define_casey :on => :size, :name => :elements
end

case []
  when 0.elements
    puts :empty
end

What you think?

Cheers
Robert
703fbc991fd63e0e1db54dca9ea31b53?d=identicon&s=25 Robert Dober (Guest)
on 2007-06-10 14:16
(Received via mailing list)
Sorry forgot the best ;)

class Object
  def identity; self end # I wanted this for a long time, maybe itself
would be a good name too
end
class Module
  def define_casey opts={}
    arg_mth = opts[:on]
    name = opts[:name]
    trans = opts[:transform]
    define_method name do
      | *args |
      cond = lambda{ |x|
  trans ? self.send(trans, *args) == x.send( arg_mth ) :
    self == x.send( arg_mth )
      }
      class << cond
  alias_method :===, :call
      end
      cond
    end
  end
end

class Integer
  define_casey :on => :size, :name => :elements
  define_casey :on => :identity, :name => :plus, :transform => :+
end

case []
  when 0.elements
    puts :empty
end

case 42
  when 41.plus( 1 )
    puts "The number"
end


Robert
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2007-06-10 20:56
(Received via mailing list)
On 10.06.2007 14:15, Robert Dober wrote:
>    trans = opts[:transform]
>    end
>    puts :empty
> end
>
> case 42
>  when 41.plus( 1 )
>    puts "The number"
> end
>
>
> Robert

I am not sure whether utility of define_casey is fully clear to me yet -
so I leave it to you to write the RCR. :-)

But I do think you're getting the hang of Ruby. :-)

Kind regards

  robert
703fbc991fd63e0e1db54dca9ea31b53?d=identicon&s=25 Robert Dober (Guest)
on 2007-06-10 21:06
(Received via mailing list)
On 6/10/07, Robert Klemme <shortcutter@googlemail.com> wrote:
> On 10.06.2007 14:15, Robert Dober wrote:

>
> I am not sure whether utility of define_casey is fully clear to me yet -
> so I leave it to you to write the RCR. :-)
No it is your code and idea, I just let ruby write it;)
No more RCRs I have already got mine, would not be fair to others;)
Seriously now, I did not write define_casey because I wanted to show
some code, I believe that your idea might be very good for some more
readable code; I am desperately looking for applications now.
>
> But I do think you're getting the hang of Ruby. :-)
Time to change to Io,   just kidding.
Robert
4828d528e2e46f7c8160c336eb332836?d=identicon&s=25 Robert Heiler (shevegen)
on 2007-06-10 21:27
If its fun, its nice :-)
In ruby i also enjoy when its short and terse (if i manage to understand
it) though :-)
703fbc991fd63e0e1db54dca9ea31b53?d=identicon&s=25 Robert Dober (Guest)
on 2007-06-10 21:53
(Received via mailing list)
On 6/10/07, Marc Heiler <shevegen@linuxmail.org> wrote:
> If its fun, its nice :-)
> In ruby i also enjoy when its short and terse (if i manage to understand
> it) though :-)

Ok it is all Robert's fault, his idea is really nice, I will explain
Define a method on integers that will implement a === which will be
called for the arg in
  case arg

he just defines a proc on which he aliased :call to :=== (that is the
genius part of this, and having the idea of course)
irb(main):001:0> class Integer
irb(main):002:1>   def elements
irb(main):003:2>     cond = lambda {|enum| self == enum.size}
irb(main):004:2>     class <<cond
irb(main):005:3>       alias :=== :call
irb(main):006:3>     end
irb(main):007:2>     cond
irb(main):008:2>   end
irb(main):009:1> end
=> nil
irb(main):010:0> case [1,2,3]
irb(main):011:1> when 3.elements
Ruby will call 3.element === [1,2,3] as you know
But 3.elements is a proc with === aliased to :call, thus
Ruby calls this_proc.call([1,2,3])
in which [1,2,3].size will be compared to self whihc of course is 3.
<snip>
As I am very lazy I want to have a shortcut for "def elements..."
I just put a method in class Module which will define such methods.
...
   define_method name do
     | *args |
** is like "def #{name} *args"
     cond = lambda{ |x|
** literal code inside def
       trans ? self.send(trans, *args) == x.send( arg_mth ) :
## forget trans
         self == x.send( arg_mth )
## this part only is corresponding to Robert's code, arg_mth is :size
in our case
## that becomes equivalent ( but way slower ) to
** self == x.size
     }
     class << cond
       alias_method :===, :call
     end
     cond
** all above is just stolen from the original idea
   end

if you call that with :name=>"elements" and on => "size" it will just
do exactly what Robert did, forget the arguments extension, that was
just to fool around even more.
This is a little bit like macros in Lisp - just better, because I can
figure it out ;).

Cheers
Robert
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2007-06-11 00:30
(Received via mailing list)
Robert Klemme wrote:
>
> A funny (and readable) way to test collection sizes just occurred to me:
...

Some more Sunday afternoon fun...

class Cond < Proc
   alias :=== :call
end

module Kernel
   def cond
     Cond.new
   end
end

class Integer
   def elements
     cond {|enum| self == enum.size}
   end
end

class Range
   def elements
     cond {|enum| self === enum.size}
   end

   def includes_it
     cond {|enum| enum.all?{|elt| self.include?(elt)}}
   end
end

class Object
   def is_included
     cond {|enum| enum.include? self}
   end
end

require 'enumerator'
INCREASING = cond {|enum| enum.enum_for(:each_cons, 2).all?{|x,y| x <
y}}

case [1,2,3]
when (3..5).elements
   puts "at least three, but no more than five, elements"
end

case [1,2,3]
when (1..4).includes_it
   puts "included in 1..4"
end

case [1,2,3]
when 2.is_included
   puts "includes 2"
end

case [1,2,3]
when INCREASING
   puts "increasing!"
end

case [1,2,3]
when 3.elements
   puts "three elements!"
when 5.elements
   puts "too much!"
else
   puts "else"
end

__END__

Output:

at least three, but no more than five, elements
included in 1..4
includes 2
increasing!
three elements!
F3b02532d4cb4855881935c002389213?d=identicon&s=25 Morton Goldberg (Guest)
on 2007-06-11 02:18
(Received via mailing list)
On Jun 10, 2007, at 6:35 AM, Robert Klemme wrote:

> irb(main):008:2>   end
> three!
> => nil
>
> :-)

I admit this is very clever. But doesn't the user pay a rather high
runtime cost in return for the coder's enjoying a small dollop of
syntactic sugar? Integer#elements is a rather expensive function for
what it delivers. Does no one recall what the late Alan Perlis wrote
in 1982 [*]:

     A LISP programmer knows the value of everything, but the cost of
nothing.

Should Ruby programmers vie for the same notoriety?

I realize you were only having a little fun and sharing your fun with
the mailing list. But when others start proposing an RCR along these
lines I get scared.

Regards, Morton

[*] No. 55 in <http://www.cs.yale.edu/quotes.html>.
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2007-06-11 09:01
(Received via mailing list)
On 11.06.2007 02:17, Morton Goldberg wrote:
> I admit this is very clever.

Thank you. :-)

> But doesn't the user pay a rather high
> runtime cost in return for the coder's enjoying a small dollop of
> syntactic sugar? Integer#elements is a rather expensive function for
> what it delivers.

Definitively.  You could optimize it by cashing those lambdas but that
introduces some additional complexity, overhead and then again you waste
more memory and...

> Does no one recall what the late Alan Perlis wrote in
> 1982 [*]:
>
>     A LISP programmer knows the value of everything, but the cost of
> nothing.

:-)

> Should Ruby programmers vie for the same notoriety?
>
> I realize you were only having a little fun and sharing your fun with
> the mailing list. But when others start proposing an RCR along these
> lines I get scared.
>
> Regards, Morton
>
> [*] No. 55 in <http://www.cs.yale.edu/quotes.html>.

I would not promote this for regular use as there is also the much more
efficient other form of "case" which can be utilized to solve this in a
more "natural" (?) and also more efficient manner:

case
   when a.size == 3
     ...
   when a[0] == "foo"
     ...
   else
     ...
end

Kind regards

  robert
C06869c119472a139eb163b72040b0db?d=identicon&s=25 Bertram Scharpf (Guest)
on 2007-06-11 10:00
(Received via mailing list)
Hi,

Am Montag, 11. Jun 2007, 16:00:06 +0900 schrieb Robert Klemme:
>     ...
> end

I use this construction quite often in SQL select
statements. There, I don't have an if-elsif. In Ruby I use
if-elsif indenting the first expression by three more
spaces.

  if    a.size == 3   then
    ...
  elsif a[0] == "foo" then
    ...
  else
    ...
  end


Bertram
This topic is locked and can not be replied to.