Подтвердить что ты не робот

Как работает лопата (<<) в 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] будет []

Может кто-нибудь объяснить, почему мое ожидание было неправильным?

4b9b3361

Ответ 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"

Причина, по которой он не возвращает массив с одним элементом каждый раз, как вы можете ожидать, заключается в том, что он хранит ссылку на значение, которое вы передаете конструктору. Это означает, что каждый раз, когда элемент нажат, он изменяет начальный массив.