Как работает лопата (<<) в Ruby Hashes?
Я проходил учебник серии Ruby Koans, когда я встретил это в about_hashes.rb
:
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
Значения в assert_equals
- это то, что ожидал учебник. Но я не мог понять, как существует разница между использованием оператора <<
и оператора =
?
Я ожидал, что:
-
hash[:one]
будет ["uno"]
-
hash[:two]
будет ["dos"]
-
hash[:three]
будет []
Может кто-нибудь объяснить, почему мое ожидание было неправильным?
Ответы
Ответ 1
Когда вы делаете hash = Hash.new([])
, вы создаете Хэш, значение по умолчанию которого является тем же самым экземпляром Array для всех ключей. Поэтому всякий раз, когда вы получаете доступ к ключу, которого не существует, вы возвращаете тот же массив.
h = Hash.new([])
h[:foo].object_id # => 12215540
h[:bar].object_id # => 12215540
Если вам нужен один массив на ключ, вы должны использовать синтаксис блока Hash.new
:
h = Hash.new { |h, k| h[k] = [] }
h[:foo].object_id # => 7791280
h[:bar].object_id # => 7790760
Изменить: Также посмотрите, что должен сказать Gazler относительно метода #<<
и того, на каком объекте вы его вызываете.
Ответ 2
Вы смешались с тем, как это работает. Во-первых, у хэша нет метода <<
, этот метод в вашем примере существует в массиве.
Причина, по которой ваш код не является ошибкой, заключается в том, что вы передаете значение по умолчанию вашему хэшу через конструктор. http://ruby-doc.org/core-1.9.3/Hash.html#method-c-new
hash = Hash.new([])
Это означает, что если ключ не существует, он возвращает массив. Если вы запустите следующий код:
hash = {}
hash[:one] << "uno"
Затем вы получите ошибку метода undefined.
Итак, в вашем примере, что происходит на самом деле:
hash = Hash.new([])
hash[:one] << "uno" #hash[:one] does not exist so return an array and push "uno"
hash[:two] << "dos" #hash[:two] does not exist, so return the array ["uno"] and push "dos"
Причина, по которой он не возвращает массив с одним элементом каждый раз, как вы можете ожидать, заключается в том, что он хранит ссылку на значение, которое вы передаете конструктору. Это означает, что каждый раз, когда элемент нажат, он изменяет начальный массив.