Ruby my_hash.key==[],但my_hash[key]给出了一个值?

Ruby my_hash.key==[],但my_hash[key]给出了一个值?,ruby,hash,Ruby,Hash,我试图演示一种情况,在创建散列时,为了设置给定键的默认值,有必要将块传递给Hash.new 为了显示可能出现的错误,我创建了以下代码,它将单个值作为参数传递给Hash.new。我希望所有外部散列键最终都持有对同一内部散列的引用,从而导致“堆”的计数混合在一起。事实上,这似乎确实发生了。但是part\u计数。每个似乎没有找到任何要迭代的键/值,并且part\u计数。键返回一个空数组。只有part\u计数[0]和part\u计数[1]为我成功检索到一个值 piles = [ [:gear, :s

我试图演示一种情况,在创建散列时,为了设置给定键的默认值,有必要将块传递给
Hash.new

为了显示可能出现的错误,我创建了以下代码,它将单个值作为参数传递给
Hash.new
。我希望所有外部散列键最终都持有对同一内部散列的引用,从而导致“堆”的计数混合在一起。事实上,这似乎确实发生了。但是
part\u计数。每个
似乎没有找到任何要迭代的键/值,并且
part\u计数。键
返回一个空数组。只有
part\u计数[0]
part\u计数[1]
为我成功检索到一个值

piles = [
  [:gear, :spring, :gear],
  [:axle, :gear, :spring],
]

# I do realize this should be:
#   Hash.new {|h, k| h[k] = Hash.new(0)}
part_counts = Hash.new(Hash.new(0))

piles.each_with_index do |pile, pile_index|
  pile.each do |part|
    part_counts[pile_index][part] += 1
  end
end

p part_counts # => {}
p part_counts.keys # => []
# The next line prints no output
part_counts.each { |key, value| p key, value }
p part_counts[0] # => {:gear=>3, :spring=>2, :axle=>1}
对于上下文,下面是我打算在“断开”代码之后显示的更正代码。
part_counts
中每个桩的零件应按原样分开
每个
也按预期工作

# ...same pile initialization code as above...
part_counts = Hash.new {|h, k| h[k] = Hash.new(0)}
# ...same part counting code as above...
p part_counts # => {0=>{:gear=>2, :spring=>1}, 1=>{:axle=>1, :gear=>1, :spring=>1}}
p part_counts.keys # => [0, 1]
# The next line of code prints:
# 0
# {:gear=>2, :spring=>1}
# 1
# {:axle=>1, :gear=>1, :spring=>1}
part_counts.each { |key, value| p key, value }
p part_counts[0] # => {:gear=>2, :spring=>1}

但是为什么在第一个示例中,
每个
键都不起作用呢?

给定给
哈希的值。new
用作默认值,但该值不会插入哈希中。因此,
part\u count
保持为空。您可以使用
part\u count[…]
获得默认值,但这对哈希没有影响,它实际上不包含密钥

当调用
part\u counts[pile\u index][part]+=1
时,
part\u counts[pile\u index]
返回默认值,并使用赋值修改该值,而不是
part\u counts

你有这样的想法:

outer = Hash.new({})
outer[1][2] = 3
p outer, outer[1]
也可以这样写:

inner = {}
outer = Hash.new(inner)

inner2 = outer[1] # inner2 refers to the same object as inner, outer is not modified
inner2[2] = 3 # same as inner[2] = 3

p outer, inner

我们将从分解这一点开始:

part_counts = Hash.new(Hash.new(0))
这等于说:

default_hash = { }
default_hash.default = 0
part_counts = { }
part_counts.default = default_hash
h = part_counts[pile_index]
h[part] += 1
稍后,你会这样说:

part_counts[pile_index][part] += 1
这等于说:

default_hash = { }
default_hash.default = 0
part_counts = { }
part_counts.default = default_hash
h = part_counts[pile_index]
h[part] += 1
您没有为哈希使用默认值的(正确)块形式,因此访问默认值不会自动激活密钥。这意味着
part\u counts[pile\u index]
不会在
part\u counts
中创建
pile\u index
键,它只会为您提供
part\u counts。默认值
,您实际上是在说:

h = part_counts.default
h[part] += 1
您没有执行任何其他操作来将键添加到
part_counts
,因此它没有键,并且:

part_counts.keys == [ ]
那么为什么
零件计数[0]
会给我们
{:gear=>3,:spring=>2,:axe=>1}
<代码>零件计数
没有任何键,特别是没有
0
键,因此:

part_counts[0]

part_counts.default
在上面访问
part\u counts[pile\u index]
的地方,您实际上只是获得了对默认值的引用,哈希不会克隆它,您将获得下次哈希将使用的整个默认值。这意味着:

part_counts[pile_index][part] += 1
这是另一种说法:

part_counts.default[part] += 1
所以实际上您只是在更改
part\u counts
的默认值。然后当你
part\u计数[0]
时,你正在访问这个修改后的默认值,还有你意外地在循环中构建的
{:gear=>3,:spring=>2,:axex=>1}