Forum: Ruby Equality question

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (Guest)
on 2007-05-08 17:33
(Received via mailing list)
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.
7a561ec0875fcbbe3066ea8fe288ec77?d=identicon&s=25 Sebastian Hungerecker (Guest)
on 2007-05-08 17:58
(Received via mailing list)
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
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (Guest)
on 2007-05-08 18:56
(Received via mailing list)
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?
1c0cd550766a3ee3e4a9c495926e4603?d=identicon&s=25 John Joyce (Guest)
on 2007-05-09 02:35
(Received via mailing list)
On May 9, 2007, at 1:55 AM, Brian Candler 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>
1c0cd550766a3ee3e4a9c495926e4603?d=identicon&s=25 John Joyce (Guest)
on 2007-05-09 02:59
(Received via mailing list)
On May 9, 2007, at 1:55 AM, Brian Candler 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.
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2007-05-09 11:06
(Received via mailing list)
On 08.05.2007 18:55, Brian Candler 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
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (Guest)
on 2007-05-09 12:57
(Received via mailing list)
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.
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (Guest)
on 2007-05-09 13:08
(Received via mailing list)
On Wed, May 09, 2007 at 06:05:11PM +0900, Robert Klemme 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.[](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.
This topic is locked and can not be replied to.