this one kept me busy for 20 minutes
if you create an array of objects and wish to call
uniq on that array, the objects of the array must supply both the eql? and hash methods. the hash method must return an integer; it's not enough that the return value be constant, it must be a Fixnum.an example
here's some code to demonstrate that you need both to be defined.
class Foo
attr_reader :s
def initialize s
@s = s
end
def inspect
"#{self.class}: @s=#{@s.inspect}"
end
alias :to_s :inspect
end
foo_array = ["a","b","b"].map{|e|Foo.new e}
puts "initially:"
puts " #{foo_array.inspect}"
puts " #{foo_array.uniq.join ', '}"
#initially:
# [Foo: @s="a", Foo: @s="b", Foo: @s="b"]
# Foo: @s="a", Foo: @s="b", Foo: @s="b"
class Foo
alias :old_hash :hash
def hash
@s.hash
end
end
puts "with hash defined:"
puts " #{foo_array.inspect}"
puts " #{foo_array.uniq.join ', '}"
#with hash defined:
# [Foo: @s="a", Foo: @s="b", Foo: @s="b"]
# Foo: @s="a", Foo: @s="b", Foo: @s="b"
class Foo
undef hash
alias :hash :old_hash
def eql? o
@s == o.s
end
end
puts "with eql? defined:"
puts " #{foo_array.inspect}"
puts " #{foo_array.uniq.join ', '}"
#with eql? defined:
# [Foo: @s="a", Foo: @s="b", Foo: @s="b"]
# Foo: @s="a", Foo: @s="b", Foo: @s="b"
class Foo
def hash
@s.hash
end
end
puts "with eql? and hash defined:"
puts " #{foo_array.inspect}"
puts " #{foo_array.uniq.join ', '}"
#with eql? and hash defined:
# [Foo: @s="a", Foo: @s="b", Foo: @s="b"]
# Foo: @s="a", Foo: @s="b"
=begin
# ruby asdf.rb
initially:
[Foo: @s="a", Foo: @s="b", Foo: @s="b"]
Foo: @s="a", Foo: @s="b", Foo: @s="b"
with hash defined:
[Foo: @s="a", Foo: @s="b", Foo: @s="b"]
Foo: @s="a", Foo: @s="b", Foo: @s="b"
with eql? defined:
[Foo: @s="a", Foo: @s="b", Foo: @s="b"]
Foo: @s="a", Foo: @s="b", Foo: @s="b"
with eql? and hash defined:
[Foo: @s="a", Foo: @s="b", Foo: @s="b"]
Foo: @s="a", Foo: @s="b"
=end
references
this forum finally led me to the idea of overriding
hash and eql?. but it also says that the only requirement for hash is that it be constant for a given state of an object. well it also needs to be a number, it turns out. otherwise you get something like this:
asdf.rb:26:in `uniq': can't convert String into Integer (TypeError)
from asdf.rb:26:in `'
infuriating. i had no idea what integer it was talking about. i see no integer! AAUGH! so it turns out that someone in a thread on the internet wasn't completely correct.
No comments:
Post a Comment