【问题标题】:Ruby hash vivification weirdnessRuby 哈希 vivification 怪异
【发布时间】:2015-07-25 04:14:56
【问题描述】:

我正在运行 ruby​​ 2.2.2:

$ ruby -v
ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]

这里我用一个键 :b 初始化一个哈希值 Hash.new({})

irb(main):001:0> a = { b: Hash.new({}) }
=> {:b=>{}}

现在,我将尝试使用键 'foo' 和值 'bar' 自动激活 a[:b][:c] 的另一个哈希

irb(main):002:0> a[:b][:c]['foo'] = 'bar'
=> "bar"

此时,我预计 a 将包含类似以下内容:

{ :b => { :c => { 'foo' => 'bar' } } }

但是,我看到的不是这样:

irb(main):003:0> a
=> {:b=>{}}
irb(main):004:0> a[:b]
=> {}
irb(main):005:0> a[:b][:c]
=> {"foo"=>"bar"}

这与以下不同:

irb(main):048:0> a = { :b => { :c => { "foo" => "bar" } } }
=> {:b=>{:c=>{"foo"=>"bar"}}}
irb(main):049:0> a
=> {:b=>{:c=>{"foo"=>"bar"}}}

那么这里发生了什么?

怀疑这与Hash.new({}) 返回默认值{} 有关,但我不确定如何解释最终结果...

【问题讨论】:

  • 请在此处阅读接受的答案:stackoverflow.com/questions/5878529/… 它对行为有很好的解释。在您的示例中,尝试打印 a[:b][:x]
  • 啊,直到我起草完我的答案才注意到这条评论。你是对的,这也很重要。谢谢!
  • 我的姑姑曾经告诉我,自动复活是 Perl 的东西。我不熟悉 Ruby 上下文中的术语。
  • @CarySwoveland 抓住了我! :)

标签: ruby hash


【解决方案1】:

很抱歉回答我自己的问题,但我知道发生了什么。

这里的答案是我们将分配给a[:b]返回的默认哈希,而不是直接分配给a[:b]

和以前一样,我们将创建一个带有单个键 b 和值 Hash.new({}) 的哈希

irb(main):068:0> a = { b: Hash.new({}) }
=> {:b=>{}}

如您所料,这应该使 a[:b][:unknown_key] 之类的东西返回一个空哈希 {},如下所示:

irb(main):070:0> a[:b].default
=> {}
irb(main):071:0> a[:b][:unknown_key]
=> {}
irb(main):072:0> a[:b].object_id
=> 70127981905400
irb(main):073:0> a[:b].default.object_id
=> 70127981905420

注意a[:b]object_id...5400,而a[:b].defaultobject_id...5420

那么当我们从原始问题做作业时会发生什么?

a[:b][:c]["foo"] = "bar"

首先,a[:b][:c] 已解决:

irb(main):075:0> a[:b][:c].object_id
=> 70127981905420

这与.default 对象相同的object_id,因为:c 被视为与上面的:unknown_key 相同!

然后,我们将具有值 'bar' 的新键 'foo' 分配到该哈希中。

确实,检查一下,我们已经有效地更改了 default 而不是 a[:b]

irb(main):081:0> a[:b].default
=> {"foo"=>"bar"}

哎呀!

【讨论】:

    【解决方案2】:

    答案可能并不像一开始看起来那么深奥,但这正是 Ruby 处理该哈希的方式。 如果您的初始哈希是:

    a = { b: Hash.new({}) } 
    b[:b][:c]['foo'] = 'bar'
    

    然后看到哈希的每个“层”只是引用下一个元素,这样:

    a                # {:b=>{}}
    a[:b]            # {}
    a[:b][:c]        # {"foo"=>"bar"}
    a[:b][:c]["foo"] # "bar"
    

    你的想法:

    { :b => { :c => { 'foo' => 'bar' } } }
    

    有点已经准确了,所以它让我认为您已经了解正在发生的事情,但由于 IRB 可能显示它的方式而不确定正在发生的事情。

    如果我遗漏了您问题的某些内容,请随时发表评论,我会修改我的答案。但我觉得在这种情况下,你比你自己认为的更了解哈希。

    【讨论】:

    • 谢谢!知道为什么a[:b] 不显示{:c=>{"foo"=>"bar"}} 吗?或 a[:b].keys 是一个空数组 [] ?如果我直接初始化我的预期哈希值,我会看到这些值。
    • 啊。 Ruby 不会“轻松”显示哈希的子级。我不想在这里成为“可能重复”的人,但是正如您在此处指出的那样,关于这种“怪异”有一个类似的问题-> stackoverflow.com/questions/1820451/… 。你的问题是一个很好的问题,但这只是嵌套哈希在 Ruby 中的行为方式。
    • 我在发帖前阅读了这个问题,但我认为它问的不是同一件事。我更新了我的帖子以显示直接分配预期哈希时的行为差异。在那种情况下,我看到了每一个键,所以我不相信这只是irb 的神器。
    • 我同意 - 这不是 irb 的产物,以及另一个问题不同的想法。我只提到另一个问题,因为它有点像当你用({}) 默认声明你的哈希时,没有出现子哈希。当 Ruby 发现您没有明确声明某些内容时,它 a) 防止进一步的声明错误出现与 'nil' 相关的问题,但是 b) 根本不显示这些子级 - 即使随后声明了这些子级(例如 a = [:b][:c]有一个实际的声明,而不是 a[:b] 明确声明)
    • 我想通了并发布了我自己问题的答案。感谢您的回复,他们肯定让我朝着正确的方向前进。
    猜你喜欢
    • 2014-03-03
    • 1970-01-01
    • 2017-02-22
    • 2011-07-01
    • 1970-01-01
    • 2021-02-05
    • 1970-01-01
    • 2014-04-09
    • 2010-11-02
    相关资源
    最近更新 更多