Ответ 1
Если вы создаете Hash
с использованием формы блока Hash.new
, блок будет выполняться каждый раз при попытке получить доступ к элементу, который на самом деле не существует. Итак, давайте просто посмотрим, что получится:
h = Hash.new { [] }
h[0] << 'a'
Первое, что оценивается здесь, - это выражение
h[0]
Что происходит, когда он оценивается? Ну, блок запускается:
[]
Это не очень интересно: блок просто создает пустой массив и возвращает его. Он ничего не делает. В частности, он никак не меняет h
: h
по-прежнему пуст.
Затем сообщение <<
с одним аргументом 'a'
отправляется в результат h[0]
, который является результатом блока, который является просто пустым массивом:
[] << 'a'
Что это делает? Он добавляет элемент 'a'
в пустой массив, но поскольку массив фактически не привязан к какой-либо переменной, он немедленно собирает мусор и уходит.
Теперь, если вы снова оцените h[0]
:
h[0] # => []
h
по-прежнему пуст, так как ничто никогда не было назначено ему, поэтому ключ 0
по-прежнему не существует, что означает, что блок снова запускается, а это означает, что он снова возвращает пустой массив (но обратите внимание, что теперь это новый, новый пустой массив).
h[0] += ['a']
Что здесь происходит? Во-первых, оператор присваивания получает desugared до
h[0] = h[0] + ['a']
Теперь оценивается h[0]
с правой стороны. И что он возвращается? Мы уже рассмотрели это: h[0]
не существует, поэтому блок запускается, блок возвращает пустой массив. Опять же, это совершенно новый, третий пустой массив. Этот пустой массив отправляет сообщение +
с аргументом ['a']
, что заставляет его возвращать еще один новый массив, который является массивом ['a']
. Затем этот массив присваивается h[0]
.
Наконец, на этом этапе:
h[0] # => ['a']
Теперь вы, наконец, положили что-то в h[0]
, поэтому, очевидно, вы получите то, что вы вложили.
Итак, чтобы ответить на вопрос, который у вас, вероятно, был, почему бы вам не разобраться, что вы вложили? Во-первых, вы ничего не вкладывали!
Если вы действительно хотите назначить хэш внутри блока, вам нужно назначить хэш внутри блока:
h = Hash.new {|this_hash, nonexistent_key| this_hash[nonexistent_key] = [] }
h[0] << 'a'
h[0] # => ['a']
На самом деле довольно легко увидеть, что происходит в вашем примере кода, если вы посмотрите на идентичность объектов. Затем вы можете видеть, что каждый раз, когда вы вызываете h[0]
, вы получаете другой массив.