I'm still a bit new to Ruby, so humor me a bit. But I discovered today
(through trial and error) that not only can Strings, numbers, and
symbols be keys for hashes, but also any object, or even a class name!
Ruby is the first language I've used in which I would have even thought
to try that, let alone it actually working:
irb(main):001:0> hsh = {}
=> {}
irb(main):002:0> obj = Object.new
=> #<Object:0x7fa4b83ab1f0>
irb(main):003:0> obj2 = Object.new
=> #<Object:0x7fa4b83a7320>
irb(main):004:0> hsh[obj] = "blah"
=> "blah"
irb(main):005:0> hsh[obj2] = "ble"
=> "ble"
irb(main):006:0> puts hsh[obj2]
ble
=> nil
irb(main):007:0> puts hsh[obj1]
NameError: undefined local variable or method `obj1' for main:Object
from (irb):7
irb(main):008:0> puts hsh[obj]
blah
=> nil
irb(main):009:0> clone = obj
=> #<Object:0x7fa4b83ab1f0>
irb(main):010:0> puts hsh[clone]
blah
=> nil
irb(main):011:0> class Cl
irb(main):012:1> end
=> nil
irb(main):013:0> hsh[Cl] = "blo"
=> "blo"
irb(main):014:0> puts hsh[obj]
blah
=> nil
irb(main):015:0> puts hsh[Cl]
blo
=> nil
irb(main):016:0> class Cl2
irb(main):017:1> end
=> nil
irb(main):018:0> hsh[Cl2] = "blu"
=> "blu"
irb(main):019:0> puts hsh[Cl]
blo
=> nil
irb(main):020:0> puts hsh[Cl2]
blu
Anyway, this raised a few related questions in my mind:
1. If the "key" taken by hash[key]= can be any object, and the key still
works even after it is aliased to another variable, does that mean that
the "key" is just a reference?
The key can be any object that implements the methods "hash" and "eql?" I'm
not sure what you mean when you say "does that mean the 'key' is just a
reference?" If you are asking what Ruby is passing around, the answer is "a
pointer to the object". That is less interesting in this case, the more
interesting thing is why it is behaving that way, which is that objects have
hash defined on them, which returns their object_id
o = Object.new
o.hash # => 2154796
o.object_id # => 2154796
I don't know if you know how hashes are implemented, but internally they map
objects to array indexes. The way Ruby does this is with the hash method,
which returns a number that correlates to the index. In your example with
obj and obj2, you can store different values there. But they are both just
empty objects, does it make sense to consider them two different keys or the
same key? With objects like this, they get different keys because they will
have different object ids. But think about a String, where each string is
different.
a1 = 'a'
a2 = 'a'
a1.object_id # => 2153018
a2.object_id # => 2153004
Do you want to have to always keep track of which string you used as the
key? No. So the hash for a string is based on the string value, in this case
"a", rather than the specific instance of "a" that was used to put it into
the hash.
a1.hash # => 14815807
a2.hash # => 14815807
2. If I pass in a number, say an Integer, as a key, does Ruby actually
use the Integer? Or does it use a reference to an Integer object?
(Numbers are objects too, right?)
There have been long discussions about this, Caleb Clousen tells me that
Fixnums are copied every time they are passed as an argument. He has gone
much deeper than I have, so presumably he knows what he is talking about,
but Ruby goes to really great lengths to hide this from you, to the point
that you must construct contrived explanations to handle the contradictions
that such models have.
I think it is best to Just consider every variable a pointer to the object.
Fixnum or not.
3. If I am allowed to pass in a class as a key, does that mean that
classes are objects too? If not, what exactly is being stored as the
key?
Yes, classes are objects:
class C
end
c = C
c == C # => true
C.class # => Class
C.object_id # => 2156420
C.hash # => 2156420
classes = [C,Array,String]
classes # => [C, Array, String]
Notice that they inherit the default hash method that just uses their object
id as the hash key.
4. When I use irb, and a line returns an object, irb shows me the
object's hexadecimal reference address (or at least, that's what it
looks like). Is there a method one can call on an object to get that
reference when one is not in irb? Just curious.
object.object_id
···
On Wed, Feb 23, 2011 at 12:42 AM, Terry Michaels <cmhoward@frigidcode.com>wrote:
-----
If you're interested, here is about as simple of an implementation of a hash
table as you can get about as simple of a hash implementation as you could get · GitHub
The purpose is to conceptually understand that hashes internally use arrays,
and see a simple example of how they achieve this. Real hashes are much more
complex (ie what happens if two objects hash to the same value? what happens
if two different objects should be considered the same hash key? what
happens when the array gets full? how is the #hash method written? etc.)