How does Array#- do comparisons?

A

Adam Bender

How does Array#- (as in, [1, 2, 3, 1, 1] - [1]) compare two objects to
see if they are equal? I would think the .eql? method, but I've
written a class that implements that method so that two different
objects with the same values are considered equal and '-' isn't
functioning as I would expect. A contrived example:

irb(main):004:0> class Hash
irb(main):005:1> def eql?(h)
irb(main):006:2> true
irb(main):007:2> end
irb(main):008:1> end
=> nil
irb(main):010:0> h1 = {'a' => 1}
=> {"a"=>1}
irb(main):011:0> h2 = {'a' => 1}
=> {"a"=>1}
irb(main):012:0> h1 == h2
=> true
irb(main):013:0> h1.eql? h2
=> true
irb(main):014:0> [h1] - [h2]
=> [{"a"=>1}]

I would expect [] as a result, just like the following:
irb(main):015:0> ["asdf"] - ["asdf"]
=> []

Thanks,

Adam
 
S

Stefano Crocco

How does Array#- (as in, [1, 2, 3, 1, 1] - [1]) compare two objects to
see if they are equal? I would think the .eql? method, but I've
written a class that implements that method so that two different
objects with the same values are considered equal and '-' isn't
functioning as I would expect. A contrived example:

irb(main):004:0> class Hash
irb(main):005:1> def eql?(h)
irb(main):006:2> true
irb(main):007:2> end
irb(main):008:1> end
=> nil
irb(main):010:0> h1 = {'a' => 1}
=> {"a"=>1}
irb(main):011:0> h2 = {'a' => 1}
=> {"a"=>1}
irb(main):012:0> h1 == h2
=> true
irb(main):013:0> h1.eql? h2
=> true
irb(main):014:0> [h1] - [h2]
=> [{"a"=>1}]

I would expect [] as a result, just like the following:
irb(main):015:0> ["asdf"] - ["asdf"]
=> []

Thanks,

Adam

It uses eql?. However, before calling it, it calls the hash method of the two
objects. If they return different values, they're considered different and
eql? is not called. If the two calls to hash return the same values, then eql?
is called. The reason is that two objects which are eql? should also have the
same hash value (see the ri documentation for Object#hash) and the comparison
of hash values is tried first because (I think) of efficiency.

This make me think one should always follow this rule:
"When you reimplement the eql? method, you should also reimplement the hash
method (or at least, make sure the hash method returns the same value for
objects which are eql?)"

This is an example:

class C

attr_reader :x
def initialize x
@x = x
end

def hash
@x.to_i
end

def eql? other
self.class == other.class and @x.eql? other.x
end

end

a = [ C.new(1), C.new(2)]
p (a - [C.new(1)])

In this case, two objects of class C are eql? if their instance variables @x
have the same value. Since hash returns @x (converted to an int), two objects
which are eql? have hash methods which return the same value.

I hope this helps

Stefano
 
R

Robert Klemme

2008/3/31 said:
How does Array#- (as in, [1, 2, 3, 1, 1] - [1]) compare two objects to
see if they are equal? I would think the .eql? method, but I've
written a class that implements that method so that two different
objects with the same values are considered equal and '-' isn't
functioning as I would expect. A contrived example:

irb(main):004:0> class Hash
irb(main):005:1> def eql?(h)
irb(main):006:2> true
irb(main):007:2> end
irb(main):008:1> end
=> nil
irb(main):010:0> h1 = {'a' => 1}
=> {"a"=>1}
irb(main):011:0> h2 = {'a' => 1}
=> {"a"=>1}
irb(main):012:0> h1 == h2
=> true
irb(main):013:0> h1.eql? h2
=> true
irb(main):014:0> [h1] - [h2]
=> [{"a"=>1}]

I would expect [] as a result, just like the following:
irb(main):015:0> ["asdf"] - ["asdf"]
=> []

Another illustration of Stefano's point:

irb(main):001:0> class Foo; end
=> nil
irb(main):002:0> [Foo.new] - [Foo.new]
=> [#<Foo:0x7ff994b0>]
irb(main):003:0> class Foo
irb(main):004:1> def eql?(o) true end
irb(main):005:1> end
=> nil
irb(main):006:0> [Foo.new] - [Foo.new]
=> [#<Foo:0x7ff80988>]
irb(main):007:0> class Foo
irb(main):008:1> def hash;0 end
irb(main):009:1> end
=> nil
irb(main):010:0> [Foo.new] - [Foo.new]
=> []

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

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,074
Latest member
StanleyFra

Latest Threads

Top