shovel(<<)运算符如何在Ruby Hashes中工作?

bit*_*its 49 ruby hash

当我遇到这个时,我正在阅读Ruby Koans教程系列about_hashes.rb:

def test_default_value_is_the_same_object
  hash = Hash.new([])

  hash[:one] << "uno"
  hash[:two] << "dos"

  assert_equal ["uno", "dos"], hash[:one]
  assert_equal ["uno", "dos"], hash[:two]
  assert_equal ["uno", "dos"], hash[:three]

  assert_equal true, hash[:one].object_id == hash[:two].object_id
end
Run Code Online (Sandbox Code Playgroud)

其中的值assert_equals实际上是教程所期望的.但我无法理解使用<<运算符和=运算符之间有何区别?

我的期望是:

  • hash[:one] 将会 ["uno"]
  • hash[:two] 将会 ["dos"]
  • hash[:three] 将会 []

有人可以解释为什么我的期望是错的吗?

Gaz*_*ler 65

你混淆了这种方式有点混乱.首先,Hash没有<<方法,您的示例中的方法存在于数组中.

您的代码没有错误的原因是因为您通过构造函数将默认值传递给哈希. http://ruby-doc.org/core-1.9.3/Hash.html#method-c-new

hash = Hash.new([])
Run Code Online (Sandbox Code Playgroud)

这意味着如果某个键不存在,那么它将返回一个数组.如果您运行以下代码:

hash = {}
hash[:one] << "uno"
Run Code Online (Sandbox Code Playgroud)

然后你会得到一个未定义的方法错误.

所以在你的例子中,实际发生的是:

hash = Hash.new([])

hash[:one] << "uno"   #hash[:one] does not exist so return an array and push "uno"
hash[:two] << "dos"   #hash[:two] does not exist, so return the array ["uno"] and push "dos"
Run Code Online (Sandbox Code Playgroud)

它没有按照您的预期每次返回一个包含一个元素的数组的原因是因为它存储了对您传递给构造函数的值的引用.这意味着每次推送元素时,它都会修改初始数组.

  • 这应该是公认的答案.更明确的解释.谢谢 (4认同)
  • 谢谢@gazler.这有助于说清楚. (2认同)

Dom*_*nef 59

当你正在hash = Hash.new([])创建一个Hash时,它的默认值是所有键的完全相同的Array实例.因此,无论何时访问不存在的密钥,都会返回相同的数组.

h = Hash.new([])
h[:foo].object_id # => 12215540
h[:bar].object_id # => 12215540
Run Code Online (Sandbox Code Playgroud)

如果每个键需要一个数组,则必须使用以下块语法Hash.new:

h = Hash.new { |h, k| h[k] = [] }
h[:foo].object_id # => 7791280
h[:bar].object_id # => 7790760
Run Code Online (Sandbox Code Playgroud)

编辑:另请参阅Gazler关于#<<方法以及您实际调用它的对象的说法.