Ruby方法Array#<<不用散列更新数组

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

 h = Hash.new{Array.new} #=> {} h[0] #=> [] h[0] < ["a"] h[0] #=> [] # why?! h[0] += ['a'] #=> ["a"] h[0] #=> ["a"] # as expected 

它是否与<<就地更改数组,而Array#+创建新实例?这一事实有关?

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

 h = Hash.new { [] } h[0] << 'a' 

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

 h[0] 

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

 [] 

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

接下来,消息<< with one argument'a 'a'被发送到h[0]的结果,这是块的结果,它只是一个空数组:

 [] << 'a' 

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

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

 h[0] # => [] 

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

 h[0] += ['a'] 

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

 h[0] = h[0] + ['a'] 

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

最后,在这一点上:

 h[0] # => ['a'] 

现在你终于真正把东西放进了h[0]所以很明显,你得到了你所放的东西。

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

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

 h = Hash.new {|this_hash, nonexistent_key| this_hash[nonexistent_key] = [] } h[0] << 'a' h[0] # => ['a'] 

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

您的代码中的问题是h[0] << 'a'一个新的数组,并在您使用h[0]索引时将其输出,但不会将修改后的数组存储在<< 'a'之后的任何位置,因为没有任务。

同时h[0] += ['a']有效,因为它相当于h[0] = h[0] + ['a'] 。 赋值( []= )有所不同。

第一种情况可能看起来令人困惑,但是当你只想在找不到密钥时从哈希接收一些不变的默认元素时它很有用。 否则,您最终可能只需通过索引就可以使用大量未使用的值填充Hash。

 h = Hash.new{ |a,b| a[b] = Array.new } h[0] << "hello world" #=> ["hello world"] h[0] #=> ["hello world"]