Как работает оператор shovel (<<) в Ruby Hashes? - PullRequest
49 голосов
/ 18 февраля 2012

Я проходил серию учебников по 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] будет []

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

Ответы [ 2 ]

64 голосов
/ 18 февраля 2012

Вы перепутали способ, которым это работает немного.Во-первых, у хэша нет метода <<, этот метод в вашем примере существует в массиве.

Причина, по которой ваш код не содержит ошибок, заключается в том, что вы передаете значение по умолчанию в свой хэшконструкторhttp://ruby -doc.org / core-1.9.3 / Hash.html # method-c-new

hash = Hash.new([])

Это означает, что если ключа не существует, то онвернет массив.Если вы запустите следующий код:

hash = {}
hash[:one] << "uno"

Тогда вы получите неопределенную ошибку метода.

Итак, в вашем примере, что на самом деле происходит:

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"

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

58 голосов
/ 18 февраля 2012

Когда вы делаете hash = Hash.new([]), вы создаете хэш, значение по умолчанию которого точно совпадает с экземпляром массива для всех ключей. Поэтому, когда вы получаете доступ к ключу, который не существует, вы получаете тот же массив.

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

Редактировать: Также посмотрите, что Газлер говорит о методе #<< и о том, какой объект вы действительно вызываете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...