我正在浏览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
它无法理解行为所以我用Google搜索并发现Strange ruby behavior when using Hash default value, e.g. Hash.new([])很好地回答了这个问题。
所以我理解它是如何工作的,我的问题是,为什么一个默认值,如一个增量的整数在使用过程中不会被改变?例如:
puts "Text please: "
text = gets.chomp
words = text.split(" ")
frequencies = Hash.new(0)
words.each { |word| frequencies[word] += 1 }
这将获取用户输入并计算每个单词的使用次数,它的工作原理是因为始终使用默认值0。
我觉得它与<<
运算符有关,但我喜欢解释。
答案 0 :(得分:105)
其他答案似乎表明行为的差异是由于Integer
是不可变的,Array
是可变的。但那是误导。不同之处并不在于Ruby的 creator 决定使一个不可变而另一个是可变的。区别在于你,程序员决定改变一个而不是另一个。
问题不在于Array
是否可变,问题是你是否变异。
只需使用Array
s即可获得上述行为。观察:
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!
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.
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 => [] }
答案 1 :(得分:3)
因为Ruby中的Array
是可变对象,所以你可以改变它的内部状态,但Fixnum
不可变。因此,当您在内部使用+=
增加值时,它会得到(假设i
是我们对Fixnum
对象的引用):
i
raw_tmp
)raw_tmp + 1
i
正如您所看到的,我们创建了新对象,i
现在引用了与开头不同的内容。
另一方面,当我们使用Array#<<
时,它的工作方式如下:
arr
因为你可以看到它更简单,但它可能会导致一些错误。其中一个是你的问题,另一个是线程竞赛,当摊位试图同时附加2个或更多元素。有时你只能用它们中的一些和内存中的th th结束,当你在数组上使用+=
时,你将摆脱这两个问题(或至少减少影响)。
答案 2 :(得分:1)
从doc,设置默认值具有以下行为:
返回默认值,如果hsh中不存在key,则由hsh返回的值。另请参阅Hash :: new和Hash#default =。
因此,每次设置frequencies[word]
时,该单个键的值都将设置为0.
两个代码块之间存在差异的原因是数组在Ruby中是可变的,而整数则不是。