不能使用数组作为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
时会发生这种情况:
-
hash
查看是否有名为'a'
的键 - 它发现不,没有这样的钥匙。
- 它看起来是否存储了要返回的默认值。
- 由于你用
Hash.new([])
构造它,它确实有这样的值,[]
并返回它。 - 现在评估
[] << 1
,这意味着hash现在存储[1]
作为在请求先前未被解决的密钥时返回的值。
如果你想要的是存储键值对,而是使用带有块的构造函数的第三种forms:
hash = Hash.new{|h, key| h[key] = []}
hash['a'] << 1
和hash['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
的默认值现在是包含值1
和2
的数组。
您看到的输出是因为IRb始终打印已评估的最后一个表达式的结果。 示例中的最后一个表达式是在Array
上调用<<
。 <<
返回其接收器,然后是整个表达式的返回值,因此IRb打印出来。