【问题标题】:ruby hash as key to a hashruby 哈希作为哈希的键
【发布时间】:2011-07-01 19:51:26
【问题描述】:

在 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 类上实现了 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 时引发异常。它没有被调用。

标签: ruby hashmap ruby-1.8


【解决方案1】:

这在 1.8.7+ 中已修复,但您也可以通过猴子补丁 1.8.6 来做正确的事 前任: https://github.com/rdp/sane/blob/master/lib/sane/hash_hashes.rb

【讨论】:

    【解决方案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 修复了

    【讨论】:

    • 那么为什么不修复Hash上的hash方法解决问题呢?
    • 因为它没有使用 ruby​​ 代码来制作其哈希码。见我上面的评论。这是来自 ruby​​ 1.8.4 但它的代码看起来像ruby-doc.org/doxygen/1.8.4/hash_8c-source.html
    • 如果您在 ruby​​ 源代码中更改 hash.c 中的哈希函数,然后重新编译,您会看到不同之处,但在更改 .hash 方法以供更高级别的 Ruby 使用时不会。
    • 看起来它归结为这个定义 [#define do_hash (key,table) (unsigned int)(*(table)->type->hash)((key)) ] 这也是意味着您不能更改成员函数指针的内部 C 表示?
    • 我认为那一定已经改变了:在 1.8.7 中,如果我将 Hash 的哈希方法更改为返回 Time.now.hash 那么它表现出与 1.8.6 相同的行为 -> 这意味着我打开 Hash 类并更改 hash 方法实际上会影响内部表示。这可能是更改为 1.8.7 的一个很好的论据 :)
    猜你喜欢
    • 1970-01-01
    • 2017-02-22
    • 2021-02-05
    • 2014-03-03
    • 2011-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-01
    相关资源
    最近更新 更多