Если вы создаете 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']
Что здесь происходит? Во-первых, оператор присваивается в
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]
, вы получаете другой массив.