[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Equality question

Brian Candler

5/8/2007 3:33:00 PM

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.

7 Answers

Sebastian Hungerecker

5/8/2007 3:58:00 PM

0

Brian Candler 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 Hungerecker
--
Ist so, weil ist so
Bleibt so, weil war so

Brian Candler

5/8/2007 4:55:00 PM

0

On Wed, May 09, 2007 at 12:58:01AM +0900, Sebastian Hungerecker wrote:
> Brian Candler 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?

John Joyce

5/9/2007 12:34:00 AM

0


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

> On Wed, May 09, 2007 at 12:58:01AM +0900, Sebastian Hungerecker wrote:
>> Brian Candler 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?
>
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>



John Joyce

5/9/2007 12:59:00 AM

0


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

> On Wed, May 09, 2007 at 12:58:01AM +0900, Sebastian Hungerecker wrote:
>> Brian Candler 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?
>

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.

Robert Klemme

5/9/2007 9:04:00 AM

0

On 08.05.2007 18:55, Brian Candler wrote:
> On Wed, May 09, 2007 at 12:58:01AM +0900, Sebastian Hungerecker wrote:
>> Brian Candler 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?

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

Brian Candler

5/9/2007 10:56:00 AM

0

On Wed, May 09, 2007 at 09:58:46AM +0900, John Joyce 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.

Brian Candler

5/9/2007 11:07:00 AM

0

On Wed, May 09, 2007 at 06:05:11PM +0900, Robert Klemme wrote:
> On 08.05.2007 18:55, Brian Candler wrote:
> >On Wed, May 09, 2007 at 12:58:01AM +0900, Sebastian Hungerecker wrote:
> >>Brian Candler 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?
>
> 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.[](input)
@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.