Метод Ruby Array# <<не обновляющий массив в хеше

Вдохновленный тем, Как я могу упорядочить хеш с массивами? Интересно, какова причина это Array#<< не будет работать правильно в следующем коде:

h = Hash.new{Array.new}
#=> {}
h[0]
#=> []
h[0] << 'a'
#=> ["a"]
h[0]
#=> [] # why?!
h[0] += ['a']
#=> ["a"]
h[0]
#=> ["a"] # as expected

Это имеет отношение к факту это << изменяет оперативный массив, в то время как Array#+ создает новый экземпляр?

14
задан Community 23 May 2017 в 10:27
поделиться

3 ответа

Если вы создаете 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']

Что здесь происходит? Сначала оператор assign сбрасывается с

h[0] = h[0] + ['a']

. Теперь вычисляется h [0] на правой стороне . И что это возвращает? Мы уже рассмотрели это: h [0] не существует, поэтому блок запускается, блок возвращает пустой массив. Опять же, это совершенно новый, третий пустой массив. Этот пустой массив получает сообщение + с аргументом ['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] , вы получаете другой массив .

33
ответ дан 1 December 2019 в 07:27
поделиться
h = Hash.new{ |a,b| a[b] = Array.new }
h[0] << "hello world"
#=> ["hello world"]
h[0]
#=> ["hello world"]
0
ответ дан 1 December 2019 в 07:27
поделиться

Проблема в вашем коде заключается в том, что h [0] << ' a ' создает новый массив и выдает его при индексировании с помощью h [0] , но не сохраняет измененный массив нигде после <<' a ' потому что нет задания.

Между тем h [0] + = ['a'] работает, потому что он эквивалентен h [0] = h [0] + ['a'] . Все дело в присваивании ([] = ).

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

3
ответ дан 1 December 2019 в 07:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: