Ruby哈希与向数组中添加元素的交互问题

4

假设我执行以下操作:

lph = Hash.new([])       #=> {}
lph["passed"] << "LCEOT" #=> ["LCEOT"]
lph                      #=> {} <-- Expected that to have been {"passed" => ["LCEOT"]}
lph["passed"]            #=> ["LCEOT"]
lph["passed"] = lph["passed"] << "HJKL"
lph #=> {"passed"=>["LCEOT", "HJKL"]}

我对此感到惊讶。有几个问题:

  1. 为什么直到我将第二个字符串推到数组中才设置它?背后发生了什么?
  2. 在ruby中,更符合惯用法的方式是什么? 我有一个哈希表、一个键和一个值,我想将其结束在与键相关联的数组中。如何将与键相关联的数组中的值第一次推入哈希表中。在以后使用该键时,我只想添加到数组中。
2个回答

6
请仔细阅读Ruby Hash.new文档——“如果随后使用不对应于哈希表条目的键访问该哈希表,则返回的值取决于用于创建哈希表的new的风格”。

new(obj) → new_hash

......如果指定了obj,则会为所有默认值使用此单个对象。

在您的示例中,您尝试将某些内容推送到与不存在的键相关联的值上,因此最终会改变您最初用于构造哈希表的同一匿名数组。

the_array = []
h = Hash.new(the_array)
h['foo'] << 1 # => [1]
# Since the key 'foo' was not found
# ... the default value (the_array) is returned
# ... and 1 is pushed onto it (hence [1]).
the_array # => [1]
h # {} since the key 'foo' still has no value.

你可能需要使用块形式:

new { |hash, key| block } → new_hash

...如果指定了一个块,它将被调用与哈希对象和键,并应返回默认值。如果需要,存储值在哈希中是块的职责。

例如:

h = Hash.new { |hash, key| hash[key] = [] } # Assign a new array as default for missing keys.
h['foo'] << 1 # => [1]
h['foo'] << 2 # => [1, 2]
h['bar'] << 3 # => [3]
h # => { 'foo' => [1, 2], 'bar' => [3] }

1
请翻译以下与编程有关的内容从英文到中文。仅返回已翻译的文本:引用文档中导致此行为的确切行为会得到加1. - Max

3

为什么只有在我将第二个字符串推入数组后才设置它?

简单来说,因为在添加第二个字符串到数组之前,你没有在哈希表中设置任何内容。

背后发生了什么?

为了看清楚背后发生了什么,让我们逐行分析:

lph = Hash.new([])       #=> {}

这创建了一个空的哈希表,配置为在访问不存在的键时返回[]对象。
lph["passed"] << "LCEOT" #=> ["LCEOT"]

这可以写成:

value = lph["passed"] #=> []
value << "LCEOT"      #=> ["LCEOT"]

我们看到 lph["passed"] 返回了如预期的 [],然后我们继续将 "LCEOT" 添加到 [] 中。
lph                  #=> {}

lph仍然是一个空的Hash。我们从未向Hash中添加过任何内容。我们添加了一些内容到其默认值中,但这并不会改变lph本身。

lph["passed"]        #=> ["LCEOT"]

这就是有趣的地方。还记得之前我们执行了value << ["LCEOT"]吗?实际上,这改变了当一个键不存在时lph返回的默认值。默认值不再是[],而是变成了["LCEOT"]。这里返回的是新的默认值。
lph["passed"] = lph["passed"] << "HJKL"

这是我们对的第一个更改。实际上,我们分配给["passed"]的是默认值(因为["passed"]仍然是不存在于中的键),并添加了“HJKL”。在此之前,默认值为["LCEOT"],之后变为["LCEOT", "HJKL"]
换句话说,lph["passed"] << "HJKL"返回["LCEOT", "HJKL"],然后被分配给["passed"]。
更具惯用性的Ruby方式是使用<<=
>> lph = Hash.new { [] }
=> {}
>> lph["passed"] <<= "LCEOT"
=> ["LCEOT"]
>> lph
=> {"passed"=>["LCEOT"]}

还要注意的是,哈希表的初始化方式发生了变化,采用了一个块而不是一个字面数组。这确保每次访问新键时都会创建并返回一个新的空数组,而不是每次使用同一个数组。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接