Weird elusive bug using Comparable

P

Pedro Côrte-Real

I have this class:

class SimpleDate
include Comparable

VALS = [:year, :month, :day]
attr_accessor *VALS

def initialize(year, month, day)
@year = year
@month = month
@day = day
end

#ideas on making this much shorter are welcome
def <=>(other)
VALS.each do |key|
v1 = self.send(key)
v2 = other.send(key)

result = (v1 <=> v2)
if result != 0
return result
end
end
0
end
end

that represents a date that might have any of the three parts not
existant. For some reason date1 == date2 returns nil when done like
this:

class SimpleDateTest < Test::Unit::TestCase
def test_comparison_with_nils
date1 = SimpleDate.new(1,nil,nil)
date2 = SimpleDate.new(1,nil,nil)

assert_not_equal date1.object_id, date2.object_id
assert date1 == date2
end
end

The code shouldn't even work because nil doesn't implement <=>. What
the hell is comparable doing behind my back?

I thought date1 == date2 was the same as (date1 <=> date2> == 0 but
apparently not.

Pedro.
 
R

Robin Stocker

Pedro said:
#ideas on making this much shorter are welcome
def <=>(other)
VALS.each do |key|
v1 = self.send(key)
v2 = other.send(key)

result = (v1 <=> v2)
if result != 0
return result
end
end
0
end
end

How about something like this (untested):

def <=>(other)
VALS.map{ |key| self.send(key) } <=> VALS.map{ |key| other.send(key) }
end
 
R

Robert Klemme

Pedro said:
I have this class:

class SimpleDate
include Comparable

VALS = [:year, :month, :day]
attr_accessor *VALS

def initialize(year, month, day)
@year = year
@month = month
@day = day
end

#ideas on making this much shorter are welcome
def <=>(other)
VALS.each do |key|
v1 = self.send(key)
v2 = other.send(key)

result = (v1 <=> v2)
if result != 0
return result
end
end
0
end
end

# shorter
SimpleDate = Struct.new :year, :month, :day do
include Comparable

def <=> (other)
self.class.members.each do |field|
result = send(field) <=> other.send(field)
return result unless result == 0
end
0
end
end
that represents a date that might have any of the three parts not
existant. For some reason date1 == date2 returns nil when done like
this:

class SimpleDateTest < Test::Unit::TestCase
def test_comparison_with_nils
date1 = SimpleDate.new(1,nil,nil)
date2 = SimpleDate.new(1,nil,nil)

assert_not_equal date1.object_id, date2.object_id
assert date1 == date2
end
end

The code shouldn't even work because nil doesn't implement <=>. What
the hell is comparable doing behind my back?

I thought date1 == date2 was the same as (date1 <=> date2> == 0 but
apparently not.

No, it's differently implemented.

Kind regards

robert
 
T

ts

"P" == =?ISO-8859-1?Q?Pedro C=F4rte-Real?= <ISO-8859-1> writes:

P> The code shouldn't even work because nil doesn't implement <=>. What
P> the hell is comparable doing behind my back?

Try it with 1.9


Guy Decoux
 
P

Pedro Côrte-Real

No, it's differently implemented.

How then? According to the docs if you implement <=> and include
Comparable you get ==. But my second assertion is failing when it
should be blowing up when trying to do nil <=> nil.

Pedro.
 
P

Pedro Côrte-Real

How about something like this (untested):

def <=>(other)
VALS.map{ |key| self.send(key) } <=> VALS.map{ |key| other.send(key) }
end

Good idea. I'm gonna try it.

Pedro.
 
P

Pedro Côrte-Real

I tried it like this:

def <=3D>(other)
p 'At the start'
result =3D VALS.map{|key| self.send(key)} <=3D> VALS.map{|key| other.se=
nd(key)}
p 'At the end'
result
end

And the strange thing is that 'At the end' isn't printed but 'At the
start' is. How can this be?

Pedro.
 
P

Pedro Côrte-Real

P> The code shouldn't even work because nil doesn't implement <=>. What
P> the hell is comparable doing behind my back?

Try it with 1.9

I don't have one around. Has this changed? I'd like to make this work
now. I thought implementing <=> and including Comparable gave me ==
but something else seems to be going on.

Pedro.
 
T

ts

"P" == =?ISO-8859-1?Q?Pedro C=F4rte-Real?= <ISO-8859-1> writes:

P> And the strange thing is that 'At the end' isn't printed but 'At the
P> start' is. How can this be?

Comparable#== catch StandardError

moulon% /usr/bin/ruby -v -e 'p NameError.ancestors'
ruby 1.8.4 (2005-12-24) [i486-linux]
[NameError, StandardError, Exception, Object, Kernel]
moulon%


moulon% ruby -v -e 'p NameError.ancestors'
ruby 1.9.0 (2006-07-14) [i686-linux]
[NameError, ScriptError, Exception, Object, Kernel, BasicObject]
moulon%



Guy Decoux
 
P

Pedro Côrte-Real

P> And the strange thing is that 'At the end' isn't printed but 'At the
P> start' is. How can this be?

Comparable#== catch StandardError

moulon% /usr/bin/ruby -v -e 'p NameError.ancestors'
ruby 1.8.4 (2005-12-24) [i486-linux]
[NameError, StandardError, Exception, Object, Kernel]
moulon%


moulon% ruby -v -e 'p NameError.ancestors'
ruby 1.9.0 (2006-07-14) [i686-linux]
[NameError, ScriptError, Exception, Object, Kernel, BasicObject]
moulon%

Ah, right, the exception is being caught. Here's a working version then:

def <=>(other)
VALS.map{|key| self.send(key)||0} <=> VALS.map{|key| other.send(key)||0}
end

Much cleaner than my first and this time it actually works.

Pedro.
 
R

Robert Klemme

Pedro said:
How then? According to the docs if you implement <=> and include
Comparable you get ==. But my second assertion is failing when it
should be blowing up when trying to do nil <=> nil.

You get == but if you implement it yourself your version will shadow the
one from Comparable:
=> #<UnboundMethod: Foo(Comparable)#==>

Now we add it to Foo:
=> [Foo, Comparable, Object, Kernel]

Kind regards

robert
 

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

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,022
Latest member
MaybelleMa

Latest Threads

Top