这是推送的一个例子:
@connections = Hash.new []
@connections[1] = @connections[1].push(2)
puts @connections # => {1=>[2]}
以下是<<
的示例@connections = Hash.new []
@connections[1] << 2
puts @connections # => {}
由于某种原因,输出(@connections)不同,但为什么?我猜它与Ruby对象模型有关吗?
也许新的哈希对象[]每次都在创建,但是没有保存?但为什么呢?
答案 0 :(得分:9)
您的代码差异不是<<
与push
的关系,而是您在一个案例中重新分配而在另一个案例中不重新分配的事实。以下两段代码是等效的:
@connections = Hash.new []
@connections[1] = @connections[1].push(2)
puts @connections # => {1=>[2]}
@connections = Hash.new []
@connections[1] = (@connections[1] << 2)
puts @connections # => {1=>[2]}
这两个一样:
@connections = Hash.new []
@connections[1].push(2)
puts @connections # => {}
@connections = Hash.new []
@connections[1] << 2
puts @connections # => {}
重新分配在这里产生影响的原因是访问默认值,不会自动为哈希添加条目。也就是说,如果您有h = Hash.new(0)
然后执行p h[0]
,则会打印0,但h
的值仍为{}
(不是{0 => 0}
)因为0没有添加到哈希。如果您执行h[0] += 1
,则会在哈希上调用[]=
方法并实际为其添加0条目,因此h
变为{0 => 1}
。
因此,当您在代码中执行@connections[1] << 2
时,您将获得默认数组并对其执行<<
,但您不会在@connections
中存储任何内容,因此它会保留{}
1}}。当您执行@connections[i] = @connections[i].push(2)
或@connections[i] = (@connections[i] << 2)
时,您正在调用[]=
,因此该条目会添加到哈希值中。
但是你应该注意,哈希每次都会返回对同一个数组的引用,所以即使你确实将条目添加到哈希中,一旦你添加了多个条目,它仍然可能不会像你期望的那样(因为所有条目都引用相同的数组):
@connections = Hash.new []
@connections[1] = @connections[1].push(2)
@connections[2] = @connections[2].push(42)
puts @connections # => {1 => [2, 42], 2 => [2, 42]}
您真正想要的是一个哈希值,每次访问新密钥时都会返回对新数组的引用,并在发生这种情况时自动为新数组添加条目。为此,您可以使用Hash.new
的块形式:
@connections = Hash.new do |h, k|
h[k] = []
end
@connections[1].push(2)
@connections[2].push(42)
puts @connections # => {1 => [2], 2 => [42]}
答案 1 :(得分:1)
请注意,当你写
时h = Hash.new |this_hash, non_existent_key| { this_hash[non_existent_key] = [] }
...只要您尝试查找不存在的密钥,Ruby就会执行该块,然后返回该块的返回值。块就像def一样,每次调用块时都会重新创建其中的所有变量(包括参数变量)。另外,请注意[]
是一个Array构造函数,每次调用它时都会创建一个新数组。
一个块返回块中执行的最后一个语句的结果,即赋值语句:
this_hash[non_existent_key] = []
并且赋值语句返回右侧,它将引用分配给散列中的键的相同Array,因此对返回的Array的任何更改都将更改散列中的Array。
另一方面,当你写:
Hash.new([])
[]构造函数创建一个新的空数组;并且该数组成为Hash.new()的参数。每次查找不存在的键时都没有阻止ruby调用,所以ruby只返回一个Array作为所有不存在的键的值 - 而且非常重要的是没有对哈希进行任何操作。