4

哈希初始化器:

# this
animals = Hash.new { [] }
animals[:dogs] << :Scooby
animals[:dogs] << :Scrappy
animals[:dogs] << :DynoMutt
animals[:squirrels] << :Rocket
animals[:squirrels] << :Secret
animals #=> {}
# is not the same as this
animals = Hash.new { |_animals, type| _animals[type] = [] }
animals[:dogs] << :Scooby
animals[:dogs] << :Scrappy
animals[:dogs] << :DynoMutt
animals[:squirrels] << :Rocket
animals[:squirrels] << :Secret
animals #=> {:squirrels=>[:Rocket, :Secret], :dogs=>[:Scooby, :Scrappy, :DynoMutt]}

我看到有人在另一个问题上发布了这些,但我不明白为什么动物在第一种情况下显得空白。如果我输入

animals[:dogs]

我得到了适当的数组。

4

3 回答 3

7

第一种形式指定为未找到的键返回默认值的块。这意味着当您调用 时,哈希animals[:dogs]中没有键,因此您的块被调用并评估您的块的结果,即。然后发生的事情是附加到那个空列表,然后很高兴地丢弃它。:dogsanimals[:dogs][]<< :Scooby:Scooby

第二种形式指定块,当请求密钥但未找到时,接收作为参数的散列本身和尚未找到的密钥。它是第一个构造函数的稍微强大的版本。不同之处在于您的块的作用。在第二种形式中,您修改散列以[]与尚未找到的键关联。所以现在它存储在哈希<< :Scooby中并将存储:Scooby在那里。进一步的调用:dog不会触发块,因为现在:dog存在于哈希中。

于 2012-03-02T00:22:35.360 回答
3

第一种情况,key不存在时返回的默认值是[]. 然后各种语句成功地将各种狗和松鼠添加到返回的数组中。

但是,从来没有为:dogs:squirrels.

在第二种情况下,块确实使用键将新值存储回哈希条目中。

这里有点有趣的一件事是,在第一种情况下,您如何不断获得一个新的空数组。答案是:您没有[]作为参数传递,而是作为块传递。这是可执行的,它被保存为一个过程。每次找不到密钥时,proc 都会再次运行并生成一个新的[].

您可以在操作中看到这一点,注意不同的对象 id 值:

irb > t = Hash.new { [] }
 => {} 
irb > t[:a].object_id
 => 2149202180 
irb > t[:a].object_id
 => 2149192500 
于 2012-03-02T00:25:23.683 回答
0

第一个失败而第二个失败的原因是传递给 Hash.new 的块。

此块用于定义当您访问尚不存在的密钥时返回的默认类型。在第一个示例中,没有条目初始化程序,因此每个新键都返回{}或一个空的Hash。Hash 没有方法<<,所以它什么也不返回。

第二种情况正常工作,因为入口初始化程序被定义为空Array。因此,在这种情况下,当您第一次访问animals[:dogs]时,它会返回[]一个 emptyArray而不是{}empty Hash。Array 确实有一个名为 的方法<<,因此它可以成功运行并将符号铲到指定键处的数组中。

希望这可以清除它。

于 2012-03-02T00:20:23.613 回答