fun with "case"

Discussion in 'Ruby' started by Robert Klemme, Jun 10, 2007.

  1. 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
     
    Robert Klemme, Jun 10, 2007
    #1
    1. Advertising

  2. Joachim Glauche, Jun 10, 2007
    #2
    1. Advertising

  3. 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
     
    Robert Klemme, Jun 10, 2007
    #3
  4. 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


    --
    Bertram Scharpf
    Stuttgart, Deutschland/Germany
    http://www.bertram-scharpf.de
     
    Bertram Scharpf, Jun 10, 2007
    #4
  5. Robert Klemme

    Gustav Paul Guest

    Robert Klemme wrote:
    >
    > 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
    >
    >

    lol, that's pretty sweet :)

    G
     
    Gustav Paul, Jun 10, 2007
    #5
  6. Robert Klemme

    Robert Dober Guest

    On 6/10/07, Robert Klemme <> 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
    >
    > Kind regards
    >
    > robert
    >
    >



    --
    You see things; and you say Why?
    But I dream things that never were; and I say Why not?
    -- George Bernard Shaw
     
    Robert Dober, Jun 10, 2007
    #6
  7. Robert Klemme

    Robert Dober Guest

    On 6/10/07, Robert Klemme <> wrote:
    >
    > 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
    >
    >

    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[:eek:n]
    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 :eek:n => :size, :name => :elements
    end

    case []
    when 0.elements
    puts :empty
    end

    What you think?

    Cheers
    Robert


    --
    You see things; and you say Why?
    But I dream things that never were; and I say Why not?
    -- George Bernard Shaw
     
    Robert Dober, Jun 10, 2007
    #7
  8. Robert Klemme

    Robert Dober Guest

    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[:eek:n]
    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 :eek:n => :size, :name => :elements
    define_casey :eek:n => :identity, :name => :plus, :transform => :+
    end

    case []
    when 0.elements
    puts :empty
    end

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


    Robert
     
    Robert Dober, Jun 10, 2007
    #8
  9. On 10.06.2007 14:15, Robert Dober wrote:
    > 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[:eek:n]
    > 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 :eek:n => :size, :name => :elements
    > define_casey :eek:n => :identity, :name => :plus, :transform => :+
    > end
    >
    > case []
    > when 0.elements
    > 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
     
    Robert Klemme, Jun 10, 2007
    #9
  10. Robert Klemme

    Robert Dober Guest

    On 6/10/07, Robert Klemme <> 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

    --
    You see things; and you say Why?
    But I dream things that never were; and I say Why not?
    -- George Bernard Shaw
     
    Robert Dober, Jun 10, 2007
    #10
  11. Robert Klemme

    Marc Heiler Guest

    If its fun, its nice :)
    In ruby i also enjoy when its short and terse (if i manage to understand
    it) though :)

    --
    Posted via http://www.ruby-forum.com/.
     
    Marc Heiler, Jun 10, 2007
    #11
  12. Robert Klemme

    Robert Dober Guest

    On 6/10/07, Marc Heiler <> 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
    --
    You see things; and you say Why?
    But I dream things that never were; and I say Why not?
    -- George Bernard Shaw
     
    Robert Dober, Jun 10, 2007
    #12
  13. 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!



    --
    vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407
     
    Joel VanderWerf, Jun 10, 2007
    #13
  14. On Jun 10, 2007, at 6:35 AM, Robert Klemme wrote:

    > 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
    >
    > :)


    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>.
     
    Morton Goldberg, Jun 11, 2007
    #14
  15. 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
     
    Robert Klemme, Jun 11, 2007
    #15
  16. Hi,

    Am Montag, 11. Jun 2007, 16:00:06 +0900 schrieb Robert Klemme:
    > 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


    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


    --
    Bertram Scharpf
    Stuttgart, Deutschland/Germany
    http://www.bertram-scharpf.de
     
    Bertram Scharpf, Jun 11, 2007
    #16
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Steve Franks
    Replies:
    2
    Views:
    1,257
    Steve Franks
    Jun 10, 2004
  2. Tee
    Replies:
    3
    Views:
    7,821
    Herfried K. Wagner [MVP]
    Jun 23, 2004
  3. Andy Fish
    Replies:
    65
    Views:
    1,766
    Mabden
    May 18, 2004
  4. dolphin
    Replies:
    4
    Views:
    323
    Jorgen Grahn
    Aug 25, 2007
  5. er
    Replies:
    2
    Views:
    509
Loading...

Share This Page