不能使用数组作为Ruby Hash的默认值?

我正在向Hash键添加项目。 我期待得到这样的结构:

{ 'a' : [1], 'b' : [2, 3, 4] } 

我使用Array来初始化Hash。

 irb> hash = Hash.new([]) => {} 

然后开始使用它:

 irb> hash['a'] < [1] irb> hash['b'] < [1, 2] 

但事实certificate:

 irb> hash => {} 

请尝试以下方法:

 hash = Hash.new{|h, k| h[k] = []} hash['a'] << 1 # => [1] hash['b'] << 2 # => [2] 

您获得意外结果的原因是您将空数组指定为默认值,但使用了相同的数组; 没有复制。 正确的方法是使用新的空数组初始化值,就像在我的代码中一样。

您使用的构造函数存储[]作为访问未知密钥时返回的默认值。 由于Array#<<修改了它的接收器因此这个最初为空的数组会增长。

要更详细地解释:

当你做hash['a'] << 1时会发生这种情况:

  1. hash查看是否有名为'a'的键
  2. 它发现不,没有这样的钥匙。
  3. 它看起来是否存储了要返回的默认值。
  4. 由于你用Hash.new([])构造它,它确实有这样的值, []并返回它。
  5. 现在评估[] << 1 ,这意味着hash现在存储[1]作为在请求先前未被解决的密钥时返回的值。

如果你想要的是存储键值对,而是使用带有块的构造函数的第三种forms:

 hash = Hash.new{|h, key| h[key] = []} 

hash['a'] << 1hash['b'] << 2不是用于创建键/值对的正确语法。 您必须使用=表示:

 hash['a'] = [] hash['a'] << 1 hash['b'] = [] hash['b'] << 2 

这应该给你哈希{'a' : [1], 'b' : [2]}

正是您期望看到的行为。

你永远不会向Hash添加任何东西,因此Hash是完全空的。 当您查找某个键时,该键将永远不存在,因此它将返回您指定为Array的默认值。

因此,您查找不存在的键'a' ,从而返回您指定为默认值的Array 。 然后,在该Array上调用<< ,它会向其附加值( 1 )。

接下来,您查找键'b' (也不存在),从而返回您指定为默认值的Array ,该Array现在包含您之前添加的元素1 。 然后,在该Array上调用<< ,将值2附加到它。

你最终得到一个仍然是空的Hash ,因为你从来没有添加任何东西。 Hash的默认值现在是包含值12的数组。

您看到的输出是因为IRb始终打印已评估的最后一个表达式的结果。 示例中的最后一个表达式是在Array上调用<<<<返回其接收器,然后是整个表达式的返回值,因此IRb打印出来。