修改默认哈希值

8
Ruby 允许你为哈希表定义默认值:
h=Hash.new(['alright'])
h['meh'] # => ["alright"]

赋值会在显示哈希时出现,但修改默认值不会。那么'bad'在哪里呢?

h['good']=['fine','dandy']
h['bad'].push('unhappy')
h # => {"good"=>["fine", "dandy"]}

'bad'会在我们明确要求时显示出来。

h['bad'] # => ["alright", "unhappy"]

修改后的默认值为什么在显示哈希值时没有显示出来?

合理的问题,但很可能是重复的。 - Andrew Grimm
2个回答

11

哈希的默认值不像你期望的那样工作。当你说h[k]时,这个过程会进行如下步骤:

  1. 如果我们有一个键k,返回它的值。
  2. 如果我们有一个哈希的默认值,返回该默认值。
  3. 如果我们有一个提供默认值的块,执行该块并返回其返回值。

请注意,第(2)和(3)步根本没有将k插入哈希表中。实际上,这个默认值将h[k]变成了这个样子:

h.has_key?(k) ? h[k] : the_default_value

因此,简单地访问不存在的键并获取默认值不会将缺失的键添加到哈希表中。

此外,任何形式为:

Hash.new([ ... ])
# or
Hash.new({ ... })

这几乎总是一个错误,因为你将会共享所有默认值的相同的默认数组或哈希表。例如,如果你这样做:

h = Hash.new(['a'])
h[:k].push('b')

然后h[:i]h[:j]等都会返回['a', 'b'],这通常不是您想要的。

我认为您正在寻找默认值的块形式:block form of the default value

h = Hash.new { |h, k| h[k] = [ 'alright' ] }

这将做两件事:

  1. 访问不存在的键将把该键添加到哈希表中,并将提供的数组作为其值。
  2. 所有的默认值都是不同的对象,因此更改一个不会影响其他的默认值。

2
发生的事情是您通过将“不高兴”pushh ['bad']中修改了哈希的默认值。您没有实际添加“bad”到哈希表中,这就是为什么在检查h时它不会显示的原因。
在提供的所有代码之后,我尝试了以下操作:
>> p h['bleh']
=> ["allright", "unhappy"]

这确实向我表明默认值已经被更改。回答你的问题"为什么修改后的默认值在显示哈希时没有出现?",你需要向其中添加一个元素,而不是只访问它:

>> h['bleh']  # Doesn't add 'bleh' to the hash
>> p h
=> {"good"=>["fine", "dandy"]} # See, no extra values

>> h['bleh'] = h.default  # Does add a new key with the default value
>> p h
=> {"good"=>["fine", "dandy"], "bleh"=>["allright", "unhappy"]}

1
哇,我完全改变了看法。但现在我对为什么会这样感到困惑。[]是哈希对象上的一个方法,返回一个结果。为什么发送push会改变默认哈希呢?是时候开始深入研究Ruby源代码了... - Marc Talbot
明白了。哈希对象仍然包含对正在更新的数组的引用。删掉我的回答。 :) - Marc Talbot
1
我假设默认值是对传递到Hash构造函数中的数组的引用。当你在其上执行'push'操作时,你直接修改的是数组本身而不是创建一个副本。举个简单的例子:h = Hash.new('hello'); h['something'] << 'aaa'; puts h.default将返回'helloaaa'。 - Jon M

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