Funtionality of 'case'-Expression

R

Ralf Müller

Salve!

Is it possible in the 'when'-part to access components or mthods of an
object specified in after 'case' ?

Something like:

case object
when object.method == 'whatever' then ...
when ohject.id%2 == 0 then ...
end

regards
ralf
 
R

Robert Klemme

Ralf Müller said:
Salve!

Is it possible in the 'when'-part to access components or mthods of an
object specified in after 'case' ?

Something like:

case object
when object.method == 'whatever' then ...
when ohject.id%2 == 0 then ...
end

You can do

case
when object.method == 'whatever' then ...
when ohject.id%2 == 0 then ...
end

Note the subtle difference. :)

Alternatively you can define a criterion like this:

crit1 = lambda {|x| x.method == 'whatever'}
class <<crit1; alias :=== :call end

case object
when crit1; ...
when crit2; ...
end

Kind regards

robert
 
R

Ralf Müller

Robert said:
You can do

case
when object.method == 'whatever' then ...
when ohject.id%2 == 0 then ...
end

Note the subtle difference. :)

Alternatively you can define a criterion like this:

crit1 = lambda {|x| x.method == 'whatever'}
class <<crit1; alias :=== :call end

case object
when crit1; ...
when crit2; ...
end

Kind regards

robert
thanks robert,
this lambda mechanism is an interessting point.
What you do, is an in-place-extension of 'class' only for its actual
instance 'object', right? And you call it '===', because 'case' needs
this functcion.
But whitch alias do you choose for 'crit2?

ralf
 
G

gabriele renzi

Ralf Müller ha scritto:
thanks robert,
this lambda mechanism is an interessting point.
What you do, is an in-place-extension of 'class' only for its actual
instance 'object', right?

He's adding that method to the singleton class of 'crit1'.
actually I prefer:

def crit2.===(z) call ; end
this may be of interest to you:
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/104332
It explains singleton classes in a lovely way :)
And you call it '===', because 'case' needs
this functcion.
But whitch alias do you choose for 'crit2?

crit2 = lambda {|x| x.id == 'whatever'}
def crit2.===(z) call ; end

but /I/ prefer:
 
B

Brian Candler

Is it possible in the 'when'-part to access components or mthods of an
object specified in after 'case' ?

Something like:

case object
when object.method == 'whatever' then ...
when ohject.id%2 == 0 then ...
end

How about:

def with(obj,&blk) obj.instance_eval(&blk); end

...

with(object) do
if method == 'whatever' then ...
elsif id%2 == 0 then ...
end
end

Regards,

Brian.
 
M

Mark Hubbart

You can do

case
when object.method == 'whatever' then ...
when ohject.id%2 == 0 then ...
end

Note the subtle difference. :)

Alternatively you can define a criterion like this:

crit1 = lambda {|x| x.method == 'whatever'}
class <<crit1; alias :=== :call end

case object
when crit1; ...
when crit2; ...
end

This sounds like a use case for the Proc.new extension proposal in the
latest ruby-dev summary... To quote:
Nowake proposed that a Proc object should be invoked with
any method name like that:

       m = Proc.new( :to_s ) { 'test1' }
       p m.to_s   # => 'test1'

So your code could be:
crit1 = Proc.new:)===){|x| x.method === 'whatever'}
case object
when crit1
end

Hmmm... I didn't like the idea before, but I might be changing my mind.

cheers,
Mark
 
B

Brian Candler

You can do
...
This sounds like a use case for the Proc.new extension proposal in the
latest ruby-dev summary... ...
So your code could be:
crit1 = Proc.new:)===){|x| x.method === 'whatever'}
case object
when crit1
end ...
Hmmm... I didn't like the idea before, but I might be changing my mind.

Let's factor out the variable, so we get

case object
when Proc.new:)===){|x| x.method == 'whatever' }
... do stuff1
when Proc.new:)===){|x| x.id%2 == 0 }
... do stuff2
end

Now, how about I write instead:

if object.method == 'whatever'
... do stuff1
elsif object.id%2 == 0
... do stuff2
end

Well, I know which one I'd prefer to read or write :)

If the underlying objective is just to factor out the common 'object.' in
each branch, then I'd still suggest

object.instance_eval {
case
when method == 'whatever'
... do stuff1
when id%2 == 0
.. do stuff2
end
}

Regards,

Brian.
 
M

Markus

...
Let's factor out the variable, so we get

case object
when Proc.new:)===){|x| x.method == 'whatever' }
... do stuff1
when Proc.new:)===){|x| x.id%2 == 0 }
... do stuff2
end

But "factoring out the variable" largely misses the point. Try it
with a concrete example:

def criterion(&b)
Proc.new :===,&b
end


non_numeric = criterion {|x| !(x.is_a? Numeric}
negative = criterion {|x| x < 0}
prime = criterion {|x| ...
case n
when non_numeric ...
when negative ...
when Float ...
when prime ...
else ...
end

This could lead to much more readable code in some circumstances. Also,
note how easily criteria could be passed around, shared or predicated,
etc.

-- Markus
 
B

Brian Candler

But "factoring out the variable" largely misses the point. Try it
with a concrete example:

def criterion(&b)
Proc.new :===,&b
end


non_numeric = criterion {|x| !(x.is_a? Numeric}
negative = criterion {|x| x < 0}
prime = criterion {|x| ...
case n
when non_numeric ...
when negative ...
when Float ...
when prime ...
else ...
end

Fair enough. I'd make those criteria constants, rather than local variables
- perhaps in all-caps to avoid confusion with classes like Float.

However, I notice you decided to wrap Proc.new:)sym) in a method - which
suggests to me that there is little benefit from having the new feature in
Proc.new, as your 'criterion' method could just as well have done the work
there.

Perhaps it makes more sense in this particular example to subclass Proc,
giving a "Proc which has an === method for use in case statements":

class Criterion < Proc
def ===(other)
call(other)
end
end
NEGATIVE = Criterion.new {|x| x < 0 }

[1,3,-4].each do |v|
puts v
case v
when NEGATIVE
puts "Negative!"
end
end

Regards,

Brian.
 
B

Brian Candler

Perhaps it makes more sense in this particular example to subclass Proc,
giving a "Proc which has an === method for use in case statements":

Or arguably, just alias Proc#=== to Proc#call in the language core for use
in case statements, in the same way as Class#=== exists primary for use in
case statements.

It's interesting to observe that these objects we're talking about are the
same as the selector blocks you'd use in Array#find and friends.

Regards,

Brian.
 
M

Markus

Perhaps it makes more sense in this particular example to subclass Proc,
giving a "Proc which has an === method for use in case statements":

class Criterion < Proc
def ===(other)
call(other)
end
end
NEGATIVE = Criterion.new {|x| x < 0 }

*smile* I'd be more enthusiastic about that if I hadn't just been
burned subclassing Proc (my whole "some Proc objects quietly exploding a
single argument if it happens to be an array bug" rant).
Fair enough. I'd make those criteria constants, rather than local variables
- perhaps in all-caps to avoid confusion with classes like Float.

What's wrong with them being variables? They are first class
citizen, after all. We can pass them around, change their value
(imagine instead of "negative" and "prime" criteria like
"acceptable_to_most_voters" or "not_too_ugly" that may have
(complicated) values constructed elsewhere) and in general do anything
we want with the values.
So why artificially constrain their "storage class"?

-- MarkusQ
 
M

Markus

Or arguably, just alias Proc#=== to Proc#call in the language core for use
in case statements, in the same way as Class#=== exists primary for use in
case statements.

Foul. The case case was adduced as _an_example_ of where the new
Proc functionality might be useful. The point of extensibility feature
such as this is to _avoid_ having to extend the language core every time
it would be useful to do whatever they do.

Alice: I'd like to add this feature.

Bob: Why would you want that?

Alice: Well, it would be handy in case C1.

Bob: Oh, good idea. We could just deal with that in the language
core. So why do you want that feature again?

Alice: Well, it would be handy in case C2.

Bob: Yes. I see. We could add that to the language core
too. But I still don't see why we need that feature.

Alice: Well, it would be handy in case C3
:

It's interesting to observe that these objects we're talking about are the
same as the selector blocks you'd use in Array#find and friends.

*smile* Isn't it though?

-- MarkusQ
 
R

Robert Klemme

Markus said:

I don't think so: we can have both. In fact I like the idea of this built
in alias very much. Currently I can't see any obstacles. In the meantime
we can do

module Kernel
private
def Criterion(&b)
class << b; alias :=== :call end; b
end
end

Alternatively we could suggest to put this method definition into the
standard lib.

Kind regards

robert
 
R

Robert Klemme

Let's factor out the variable, so we get

case object
when Proc.new:)===){|x| x.method == 'whatever' }
... do stuff1
when Proc.new:)===){|x| x.id%2 == 0 }
... do stuff2
end

This is inefficient. Using a constant is more efficient and IMHO more
readable:

EVEN = Proc.new:)===){|x| x % 2 == 0 }

case foo
when EVEN
...
end

Kind regards

robert
 
B

Brian Candler

What's wrong with them being variables? They are first class
citizen, after all. We can pass them around, change their value

I was just thinking of efficiency. You can declare

NEGATIVE = Criterion.new { |x| x < 0 }

in a module or class definition, and a single persistent object is created.
However if you do this in a local variable, which by necessity is within a
method body, then every time you call that method a fresh Criterion object
will be created.

The constant also lets you share the same Criterion object between methods.
(imagine instead of "negative" and "prime" criteria like
"acceptable_to_most_voters" or "not_too_ugly" that may have
(complicated) values constructed elsewhere) and in general do anything
we want with the values.
So why artificially constrain their "storage class"?

Sure, if you need a different Criterion object at different times, then
refer to it from a (local variable / instance variable / whatever)

Regards,

Brian.
 

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
474,159
Messages
2,570,879
Members
47,416
Latest member
LionelQ387

Latest Threads

Top