Equality question


#1

I am trying to use complex values as hash keys (they are actually method
calls caught by method_missing). However strange things are happening.
Can
someone explain what’s happening here please where marked?

confs = {“default” => “! Config\nfoo=bar\n”}
hash = {
[:firmware_id] => “12.4.8”,
[:uptime] => 0,
[:read_configs] => confs,
[:defuzz_configs, confs] => {“default” => “foo=bar\n”},
[:defuzz_configs, {“conf”=>“foo.txt”}] => {“default” =>
“foo=bar\n”},
}
p hash.size

key1 = [:defuzz_configs, confs]
p hash[key1] # this works

key2 = [:defuzz_configs, {“conf”=>“foo.txt”}]
p hash[key2] # this prints nil ?? <<<<<<<<<<<

o1 = [:defuzz_configs, {“conf”=>“foo.txt”}]
o2 = [:defuzz_configs, {“conf”=>“foo.txt”}]
p o1 == o2 # prints true
p o1.hash == o2.hash # prints false ?? <<<<<<<<<<<

Thanks!

Brian.


#2

Brian C. wrote:

o1 = [:defuzz_configs, {“conf”=>“foo.txt”}]
o2 = [:defuzz_configs, {“conf”=>“foo.txt”}]
p o1 == o2 # prints true
p o1.hash == o2.hash # prints false ?? <<<<<<<<<<<

That’s because the Hashs in o1 and o2 are two different objects.

hash={:bla=>:blubb}
=> {:bla=>:blubb}

a1=[:a,hash]
=> [:a, {:bla=>:blubb}]

a2=[:a,hash]
=> [:a, {:bla=>:blubb}]

a3=[:a,{:bla=>:blubb}]
=> [:a, {:bla=>:blubb}]

a1==a3
=> true

a1.hash==a3.hash
=> false

a1.hash==a2.hash
=> true

hash[:blu]=:blo
=> :blo

p a1,a2,a3
[:a, {:bla=>:blubb, :blu=>:blo}]
[:a, {:bla=>:blubb, :blu=>:blo}]
[:a, {:bla=>:blubb}]
=> nil

a1==a3
=> false

a1==a2
=> true

HTH,
Sebastian H.


#3

On Wed, May 09, 2007 at 12:58:01AM +0900, Sebastian H. wrote:

Brian C. wrote:

o1 = [:defuzz_configs, {“conf”=>“foo.txt”}]
o2 = [:defuzz_configs, {“conf”=>“foo.txt”}]
p o1 == o2 # prints true
p o1.hash == o2.hash # prints false ?? <<<<<<<<<<<

That’s because the Hashs in o1 and o2 are two different objects.

So hashes and arrays don’t behave the same in this regard?

o1 = [“hello”, [“world”]]
o2 = [“hello”, [“world”]]
p o1 == o2
p o1.hash == o2.hash # prints true

o1 = {“key”=>“hello”}
o2 = {“key”=>“hello”}
p o1 == o2
p o1.hash == o2.hash # prints false

I wonder why this is?


#4

On May 9, 2007, at 1:55 AM, Brian C. wrote:

I wonder why this is?

I’m a bit confused by this too. I’ve got seemingly conflicting stuff
here:

irb(main):001:0> h1 = [‘hi’, [‘dude’]]
=> [“hi”, [“dude”]]
irb(main):002:0> h2 = [‘hi’, [‘dude’]]
=> [“hi”, [“dude”]]
irb(main):003:0> h1.class
=> Array
irb(main):004:0> h2.class
=> Array
irb(main):005:0> h1 == h2
=> true
irb(main):006:0> h1[1] == h2[1]
=> true
irb(main):007:0> h3 = h2
=> [“hi”, [“dude”]]
irb(main):009:0> h2 == h3
=> true
irb(main):010:0> hash1 = {‘greet’ => ‘hi’}
=> {“greet”=>“hi”}
irb(main):011:0> hash1.class
=> Hash
irb(main):012:0> hash2 = hash1
=> {“greet”=>“hi”}
irb(main):013:0> hash1 == hash2
=> true
irb(main):014:0> hash1[1]
=> nil
irb(main):016:0> hash1.inspect
=> “{“greet”=>“hi”}”
irb(main):017:0> hash1.class
=> Hash
irb(main):019:0> ‘greet’
=> “greet”
irb(main):020:0> hash1[‘greet’]
=> “hi”
irb(main):025:0> hash1[‘greet’] == hash2[‘greet’]
=> true
irb(main):026:0> p h1 == h2
true
=> nil
irb(main):027:0> p hash1 == hash2
true
=> nil
irb(main):028:0> p hash1
{“greet”=>“hi”}
=> nil
irb(main):029:0> p hash1.hash
1773356
=> nil
irb(main):030:0> p hash1.to_hash
{“greet”=>“hi”}
=> nil
irb(main):031:0> p hash2.hash
1773356
=> nil
irb(main):032:0> hash1 == hash2
=> true
irb(main):033:0> hash1.hash == hash2.hash
=> true
irb(main):034:0> o1 = {“key” => “hi”}
=> {“key”=>“hi”}
irb(main):035:0> o2 = {“key” => “hi”}
=> {“key”=>“hi”}
irb(main):037:0> p o1 == o2
true
=> nil
irb(main):038:0> o1.hash
=> 1844056
irb(main):039:0> o2.hash
=> 1830256
irb(main):040:0> o1 = {“name” => “bob”}
=> {“name”=>“bob”}
irb(main):041:0> o2 = {“name” => “bob”}
=> {“name”=>“bob”}
irb(main):042:0> o1 == o2
=> true
irb(main):043:0> o1.hash
=> 1769496
irb(main):044:0> o2.hash
=> 1757086
irb(main):045:0> h1.hash
=> -382289162
irb(main):046:0> h2.hash
=> -382289162
irb(main):047:0> hash1.hash
=> 1773356
irb(main):048:0> hash2.hash
=> 1773356
irb(main):049:0>


#5

On May 9, 2007, at 1:55 AM, Brian C. wrote:

I wonder why this is?

it’s worth exploring further! (the ruby cookbook has a good section
on ruby’s hashes)
I made two hashes like yours.

irb(main):011:0> o1.id
=> 1611054
irb(main):012:0> o2.id
=> 1586104
irb(main):013:0> o1.hash
=> 1611054
irb(main):014:0> o2.hash
=> 1586104

As you can see the two hashes don’t share the same object id, .hash
method of objects creates a hash value for the object (confusing
terminology, I know) but with hashes , that hash value just happens
to be the same as the object id.
They may contain (references to) the same objects, so that makes
these two hashes look a lot alike, but they’re as different as you
and your clone.

What’s more confusing? Try messing with symbols in your hashes. Then
wrap your head around it.
I finally got a handle on symbols. They’re like hard links in unix.
little wormholes if you will.


#6

On Wed, May 09, 2007 at 09:58:46AM +0900, John J. wrote:

What’s more confusing? Try messing with symbols in your hashes. Then
wrap your head around it.
I finally got a handle on symbols. They’re like hard links in unix.
little wormholes if you will.

That I don’t have a problem with.

A symbol is basically an immutable string, which is also a singleton.

That is,

a = :foo
b = :foo
c = "foo".to_sym

will always return the exact same object for a, b and c.


#7

On 08.05.2007 18:55, Brian C. wrote:

o1 = [“hello”, [“world”]]
o2 = [“hello”, [“world”]]
p o1 == o2
p o1.hash == o2.hash # prints true

o1 = {“key”=>“hello”}
o2 = {“key”=>“hello”}
p o1 == o2
p o1.hash == o2.hash # prints false

I wonder why this is?

Probably because Hashes store not only key value pairs but also default
value and an optionally block that is invoked if an element is missing
from the Hash. Now, blocks are difficult to compare in itself but the
question here is: on what basis do you consider Hashes equivalent? Do
you include the default object or not etc. As far as I remember this
has been discussed on the list in the past to some extent. The solution
is of course to use your own class for your keys. Since they seem to be
pretty complex that’s a good idea anyway.

Kind regards

robert


#8

On Wed, May 09, 2007 at 06:05:11PM +0900, Robert K. wrote:

I wonder why this is?

Probably because Hashes store not only key value pairs but also default
value and an optionally block that is invoked if an element is missing
from the Hash.

OK, then I’m missing some subtlety between hash1 == hash2 (which is
true),
and hash1.eql? hash2 (which is false).

But given just arrays, and arrays of arrays, array1 == array2 and
array1.eql? array2 are both true. And so is array1.hash == array2.hash

So it appears that someone has decided that hashes cannot be used
usefully
as hash keys, but I cannot see why.

The solution
is of course to use your own class for your keys. Since they seem to be
pretty complex that’s a good idea anyway.

I solved my problem by doing a linear search over the hash to find the
entry, which seems silly, but it works.

# Was: return @output[input]
@output.each { |(k,v)| return v if k == input }

Making “my own class” was not an option. I was building a small mock
object,
and the hash key is whatever method_missing receives. If the caller of
the
mock object passes in a hash, then a hash is what I get.

class Mock
def self.prepare(output)
@output = output
end
def self.[]=(input,output)
@output[input] = output
end
def self.
@output.each { |(k,v)| return v if k == input }
raise “No response for #{input.inspect}”
end

attr_reader :actions
def initialize(*args)
@actions = [[:initialize] + args]
end
def method_missing(*args)
# STDERR.puts “#{args.inspect} => #{self.class[args].inspect}”
@actions << args
self.class[args]
end
end

Mock.prepare({
[:flurble] => ‘boo’,
})

a = Mock.new(123)
res = a.flurble
p res
p a.actions

Yes, I know there are other mock solutions out there. I would rather
just
write something which is (a) small, and (b) does exactly what I need.

Regards,

Brian.