Ruby散列默认值行为

Jak*_*ers 40 ruby hash

我正在浏览Ruby Koans,我打#41,我相信这是:

def test_default_value_is_the_same_object
  hash = Hash.new([])

  hash[:one] << "uno"
  hash[:two] << "dos"

  assert_equal ["uno","dos"], hash[:one]
  assert_equal ["uno","dos"], hash[:two]
  assert_equal ["uno","dos"], hash[:three]

  assert_equal true, hash[:one].object_id == hash[:two].object_id
end
Run Code Online (Sandbox Code Playgroud)

它无法理解行为所以我用谷歌搜索它并发现使用哈希默认值时的奇怪的红宝石行为,例如Hash.new([])很好地回答了这个问题.

所以我理解它是如何工作的,我的问题是,为什么一个默认值,如一个增量的整数在使用过程中不会被改变?例如:

puts "Text please: "
text = gets.chomp

words = text.split(" ")
frequencies = Hash.new(0)
words.each { |word| frequencies[word] += 1 }
Run Code Online (Sandbox Code Playgroud)

这将采用用户输入并计算每个单词的使用次数,它的工作原理是因为始终使用默认值0.

我觉得它与<<操作员有关,但我喜欢解释.

Jör*_*tag 106

其他答案似乎表明行为的差异是由于Integers是不可变的而且Array是可变的.但那是误导.不同之处并不是Ruby 的创建者决定使一个不可变,另一个是可变的.不同的是,你,程序员决定改变一个而不是另一个.

问题不在于Arrays是否可变,问题在于你是否可以改变它.

只需使用Arrays ,您就可以获得上面看到的两种行为.注意:

一个默认Array有突变

hsh = Hash.new([])

hsh[:one] << 'one'
hsh[:two] << 'two'

hsh[:nonexistent]
# => ['one', 'two']
# Because we mutated the default value, nonexistent keys return the changed value

hsh
# => {}
# But we never mutated the hash itself, therefore it is still empty!
Run Code Online (Sandbox Code Playgroud)

一个默认Array没有变异

hsh = Hash.new([])

hsh[:one] += ['one']
hsh[:two] += ['two']
# This is syntactic sugar for hsh[:two] = hsh[:two] + ['two']

hsh[:nonexistant]
# => []
# We didn't mutate the default value, it is still an empty array

hsh
# => { :one => ['one'], :two => ['two'] }
# This time, we *did* mutate the hash.
Run Code Online (Sandbox Code Playgroud)

一个新的,Array每次都有不同的变异

hsh = Hash.new { [] }
# This time, instead of a default *value*, we use a default *block*

hsh[:one] << 'one'
hsh[:two] << 'two'

hsh[:nonexistent]
# => []
# We *did* mutate the default value, but it was a fresh one every time.

hsh
# => {}
# But we never mutated the hash itself, therefore it is still empty!


hsh = Hash.new {|hsh, key| hsh[key] = [] }
# This time, instead of a default *value*, we use a default *block*
# And the block not only *returns* the default value, it also *assigns* it

hsh[:one] << 'one'
hsh[:two] << 'two'

hsh[:nonexistent]
# => []
# We *did* mutate the default value, but it was a fresh one every time.

hsh
# => { :one => ['one'], :two => ['two'], :nonexistent => [] }
Run Code Online (Sandbox Code Playgroud)