Ruby方法Array#<<不用哈希更新数组

Mla*_*vić 14 ruby arrays hash

灵感来自我如何用数组编组哈希?我想知道Array#<<在以下代码中无法正常工作的原因是什么:

h = Hash.new{Array.new}
#=> {}
h[0]
#=> []
h[0] << 'a'
#=> ["a"]
h[0]
#=> [] # why?!
h[0] += ['a']
#=> ["a"]
h[0]
#=> ["a"] # as expected
Run Code Online (Sandbox Code Playgroud)

它是否与<<在就地更改数组的事实有关,同时Array#+创建了一个新实例?

Jör*_*tag 33

如果Hash使用块形式创建Hash.new,则每次尝试访问实际不存在的元素时都会执行块.那么,让我们看看会发生什么:

h = Hash.new { [] }
h[0] << 'a'
Run Code Online (Sandbox Code Playgroud)

这里评估的第一件事就是表达式

h[0]
Run Code Online (Sandbox Code Playgroud)

评估后会发生什么?好吧,块运行:

[]
Run Code Online (Sandbox Code Playgroud)

这不是很令人兴奋:块只是创建一个空数组并返回它.它没有做任何其他事情.特别是,它不会h以任何方式改变:h仍然是空的.

接下来,<<带有一个参数的消息'a'被发送到结果,h[0]该结果是块的结果,这只是一个空数组:

[] << 'a'
Run Code Online (Sandbox Code Playgroud)

这是做什么的?它将元素添加'a'到空数组中,但由于数组实际上并未分配给任何变量,因此它会立即被垃圾收集并消失.

现在,如果h[0]再次评估:

h[0] # => []
Run Code Online (Sandbox Code Playgroud)

h仍然是空的,因为没有任何东西被分配给它,因此密钥0仍然不存在,这意味着块再次运行,这意味着它再次返回一个空数组(但请注意,它是一个全新的,不同的空数组现在).

h[0] += ['a']
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?首先,操作员分配变得卑鄙

h[0] = h[0] + ['a']
Run Code Online (Sandbox Code Playgroud)

现在,h[0]右侧边被评估.它又回归了什么?我们已经讨论了这个:h[0]不存在,因此块运行,块返回一个空数组.同样,这是一个全新的第三个空数组.这个空数组被发送+带有参数的消息['a'],这导致它返回另一个新数组,即数组['a'].然后将此数组分配给h[0].

最后,在这一点上:

h[0] # => ['a']
Run Code Online (Sandbox Code Playgroud)

现在你终于实际上放了一些东西h[0],显然,你得到了你投入的东西.

所以,为了回答你可能有的问题,你为什么不把它放进去?你没有把任何东西放在首位!

如果你真的想要分配给块内的哈希,你必须分配给块内的哈希:

h = Hash.new {|this_hash, nonexistent_key| this_hash[nonexistent_key] = [] }
h[0] << 'a'
h[0] # => ['a']
Run Code Online (Sandbox Code Playgroud)

如果你查看所涉及对象的身份,实际上很容易看到你的代码示例中发生了什么.然后你可以看到每次调用时h[0],你会得到一个不同的数组.