I went a little different route, but that was indeed the problem. I just defined == in my class, which I guess was comparing the class of the object instead of the object_id. I did:
def == obj
self.equal?(obj)
end
If you have time, or if someone else does, is there a quick explanation of why this happened in this instance,
If you just defined == the way you presented above then the issue should not have occurred because equal? tests for identity. It's difficult to comment on fragments when the whole code is not presented.
but not with similar classes that function fine with essentially duplicate methods of adding to and deleting from arrays?
I am not sure what you mean by this. Can you elaborate?
Hopefully I can avoid future problems if I understand what's happening (I'm a hobby programmer, so I lack some technical expertise).
There are some concepts you should keep in mind that have some similarities and will typically wreck havoc on your code when confused. Both define relations (in the mathematical sense) on objects.
First, there is "identity". Two objects are identical when they are actually just one, i.e. the same instance.
Then, there is equivalence. Equivalence is defined per class. For example two strings containing the same sequence of characters are equivalent. Sometimes only identical instances are equivalent.
Now, these different concepts are implemented in Ruby via different methods:
eql? and == implement equivalence
equal? implements identity
Most containers (an Array is a container) use equivalence, namely implemented via eql? to test whether some objects match (e.g. for deletion), because it is the more flexible and more useful concept. (Think of an Array of Strings and you want to delete one of them with a certain character sequence, you would want to provide a string with that sequence as template and not the exact same object in the array - which you might not know beforehand.)
Now, there is a slight twist: since for some algorithms it's not efficient to compare something against all elements in the container (for example, Array#uniq would have to compare every element of the array with every other element which is O(n*n), i.e. if you double the elements in the Array you quadruple the number of comparisons necessary). In those cases (unfortunately they are not all documented) typically a Hash is used behind the scenes. For objects to work properly as Hash key methods eql? *and* hash need to be implemented properly.
Consequence is, that you should always implement eql? and hash (and also == for consistency) reasons *if* you plan to use instances of your class in these circumstances *and* want to define equivalence different than via identity (which happens to be the default implementation in class Object). Typically you will choose some fields for this and you must also make sure that equivalent instances yield the same (!) hash code. Normally you do that by applying some math operation (binary XOR is frequently used, because it's fast and guarantees that all values used influence the result) on the hash values of those members that you determine as key elements for equivalence.
The easiest way to do that is by using Struct, because that will generate a class with all the necessary methods. Example:
# name and age are key for Foo
Foo = Struct.new :name, :age do
attr_accessor :unimportant_other_attribute
end
irb(main):007:0> f1 = Foo.new("a", 10)
=> #<struct Foo name="a", age=10>
irb(main):008:0> f2 = Foo.new("a", 10)
=> #<struct Foo name="a", age=10>
irb(main):009:0> f1.hash
=> -2186440
irb(main):010:0> f2.hash
=> -2186440
irb(main):011:0> f1.eql? f2
=> true
irb(main):012:0> f1 == f2
=> true
irb(main):013:0> f1.equal? f2
=> false
irb(main):014:0> f1.unimportant_other_attribute = "bar"
=> "bar"
irb(main):015:0> f1.eql? f2
=> true
irb(main):016:0> f1.name = "hello"
=> "hello"
irb(main):017:0> f1.eql? f2
=> false
There is another thing you should be aware: numbers in Ruby actually implement *two* different equivalence relations:
irb(main):018:0> 1 == 1.0
=> true
irb(main):019:0> 1.eql? 1.0
=> false
irb(main):020:0> 1.hash
=> 3
irb(main):021:0> 1.0.hash
=> 233071
But most classes treat == and eql? synonym.
Next week we'll dive into ordering and operator <=>. :-))
Kind regards
robert
···
On 18.08.2007 11:44, Matthew B Gardner wrote: