Fun with "case"


#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

:slight_smile:

Kind regards

robert


#2

Why not do a simply use

case [1,2,3].size

instead?


#3

On 10.06.2007 12:49, Joachim G. 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). :slight_smile:

Kind regards

robert


#4

Hi,

Am Sonntag, 10. Jun 2007, 19:49:43 +0900 schrieb Joachim G.:

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


#5

On 6/10/07, Robert K. removed_email_address@domain.invalid wrote:

On 10.06.2007 12:49, Joachim G. 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). :slight_smile:
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


#6

On 6/10/07, Robert K. removed_email_address@domain.invalid wrote:

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

:slight_smile:

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 :wink:
Please note the absence of “@” in my code :wink:

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


#7

Robert K. wrote:

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

:slight_smile:

Kind regards

robert

lol, that’s pretty sweet :slight_smile:

G


#8

Sorry forgot the best :wink:

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


#9

On 6/10/07, Robert K. removed_email_address@domain.invalid wrote:

On 10.06.2007 14:15, Robert D. 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. :slight_smile:
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. :slight_smile:
Time to change to Io, just kidding.
Robert


#10

On 10.06.2007 14:15, Robert D. 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. :slight_smile:

But I do think you’re getting the hang of Ruby. :slight_smile:

Kind regards

robert


#11

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


#12

On 6/10/07, Marc H. removed_email_address@domain.invalid wrote:

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

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.

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


#13

On Jun 10, 2007, at 6:35 AM, Robert K. wrote:

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

:slight_smile:

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.


#14

Robert K. 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!


#15

Hi,

Am Montag, 11. Jun 2007, 16:00:06 +0900 schrieb Robert K.:

...

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


#16

On 11.06.2007 02:17, Morton G. wrote:

I admit this is very clever.

Thank you. :slight_smile:

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.

:slight_smile:

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