Ruby中的哈希作为哈希键

4

在 Ruby 1.8.6 中遇到了以下奇怪的行为,在 1.8.7 中似乎正常工作。有人知道是什么原因引起的吗?

h = {}
key_1 = {1 => 2}
key_2 = {1 => 2}
h[key_1] = 3
p key_1 == key_2 # => true
p h.has_key?(key_2) # => expect true, get false, wtf?

我曾以为这是由于Hash类上哈希方法的实现所致。

p [key_1.hash, key_2.hash] # => [537787070, 537787060] (different)

但是,即使我覆盖了 Hash 的哈希方法

class Hash
  def hash
    return self.keys.hash + self.values.hash
  end
end

p [key_1.hash, key_2.hash] # => [8,8] (same
p h.has_key?(key_2)        # => false

这是一个在线Ruby 1.8.6解释器的代码片段链接,您可以查看其结果:http://codepad.org/7nCYMP4w


你正在覆盖的那段代码并没有做你认为它在做的事情。当 Ruby 访问和散列东西时,它使用的是 Ruby 本身的 C 代码。为了证明这一点,请尝试在你覆盖的 #hash 方法中抛出一个异常。它并没有被调用。 - Michael Papile
2个回答

2
答案是因为在Ruby 1.8.6中,哈希键的哈希编码算法已经被破坏了。更多信息请参考:http://paulbarry.com/articles/2009/09/14/why-rails-3-will-require-ruby-1-8-7
编辑:下面是一个示例,展示了Ruby内部不会调用.hash方法:
 class Hash
    def hash
       raise
    end
 end

 {1=>1}.hash
 RuntimeError: 
from (irb):12:in `hash'
from (irb):17

 h = {1=>2}
 {1=>2}
 h[1]
 2

在这方面,Ruby 1.8.6存在问题,如果有一种纯粹的Ruby方法来解决它(例如打开Hash),人们会使用它。这个问题在1.8.7中被修复了。


那么为什么在哈希上修复哈希方法不能解决问题呢? - Jamie Cook
因为它没有使用 Ruby 代码来生成其哈希码。请参见我的上面的评论。这是来自 Ruby 1.8.4 的,但它看起来像这样:http://www.ruby-doc.org/doxygen/1.8.4/hash_8c-source.html。 - Michael Papile
如果您在 Ruby 源代码中的 hash.c 文件中更改哈希函数并重新编译,您将会看到差异,但是更改 .hash 方法对于更高级别的 Ruby 使用则不会产生影响。 - Michael Papile
1
看起来问题就在这个定义上:[ #define do_hash (key,table) (unsigned int)(*(table)->type->hash)((key)) ] 这是否意味着您无法更改成员函数指针的内部 C 表示形式? - Jamie Cook
我认为那一定是发生了变化:在1.8.7中,如果我更改Hash的哈希方法以返回Time.now.hash,则它会表现出与1.8.6相同的行为->这意味着我打开Hash类并更改哈希方法实际上影响了内部表示。这可能是改变到1.8.7的一个很好的理由 :) - Jamie Cook
更好的选择是转换到1.9.2 :) 我一开始对1.9并不感冒,但现在它太有吸引力了,不转换都不行。 - Michael Papile

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接