fun with "case"

R

Robert Klemme

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
 
R

Robert Klemme

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
 
B

Bertram Scharpf

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
 
G

Gustav Paul

Robert said:
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
 
R

Robert Dober

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
 
R

Robert Dober

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
 
R

Robert Dober

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
 
R

Robert Klemme

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
 
R

Robert Dober

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
 
M

Marc Heiler

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

Robert Dober

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
 
J

Joel VanderWerf

Robert said:
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!
 
M

Morton Goldberg

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>.
 
R

Robert Klemme

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
 
B

Bertram Scharpf

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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top