2012-03-02 1 views
4

Хэш Инициализаторы:Руби Hash Инициализаторы

# this 
animals = Hash.new { [] } 
animals[:dogs] << :Scooby 
animals[:dogs] << :Scrappy 
animals[:dogs] << :DynoMutt 
animals[:squirrels] << :Rocket 
animals[:squirrels] << :Secret 
animals #=> {} 
# is not the same as this 
animals = Hash.new { |_animals, type| _animals[type] = [] } 
animals[:dogs] << :Scooby 
animals[:dogs] << :Scrappy 
animals[:dogs] << :DynoMutt 
animals[:squirrels] << :Rocket 
animals[:squirrels] << :Secret 
animals #=> {:squirrels=>[:Rocket, :Secret], :dogs=>[:Scooby, :Scrappy, :DynoMutt]} 

Я видел, кто-то после них на другой вопрос, но я не понимаю, почему животные появляется пустой в первом случае. Если я напечатаю

animals[:dogs] 

Я получаю соответствующий массив.

+0

Вы можете связать с «другой вопрос»? –

+1

@Andrew: Это выглядит так же, как http://stackoverflow.com/q/9492889/479863, ответ по существу, по крайней мере один и тот же. –

+0

Другой вопрос: http://stackoverflow.com/questions/613985/common-ruby-idioms –

ответ

7

Первая форма указывает блок, который возвращает значение по умолчанию для ключа, который не найден. Это означает, что при вызове animals[:dogs] в хэше нет ключа :dogs, поэтому ваш блок вызывается, а animals[:dogs] оценивает результат вашего блока, то есть []. Затем происходит то, что << :Scooby добавляет :Scooby в этот пустой список, который затем успешно отбрасывается.

Вторая форма указывает блок, который, когда ключ запрошен и не найден, принимает в качестве параметров сам хеш и ключ, который не был найден. Это немного более мощная версия первого конструктора. Разница в том, что делает ваш блок. В этой второй форме вы должны ввести [] с ключом, который не был найден. Итак, теперь он хранится внутри хеша, и << :Scooby будет хранить :Scooby. Дальнейшие вызовы для :dog не будут запускать блок, потому что теперь в хеше существует :dog.

+1

Дополнительная информация здесь: http://www.ruby-doc.org/core-1.9.3/Hash.html –

0

Причина, по которой первое не удается, а второе - не из-за блока, который передается в Hash.new.

Этот блок служит для определения типа по умолчанию, возвращаемого при доступе к ключу, который еще не существует. В первом примере нет инициализатора ввода, поэтому каждый новый ключ возвращает {} или пустой Hash. Хэш не имеет метода <<, поэтому он ничего не возвращает.

Второй случай работает правильно, поскольку инициализатор записи определен как пустой Array. Итак, в этом случае, когда вы впервые получаете доступ к animals[:dogs], он возвращает [] пустой Array вместо {} пустой Hash. У массива есть метод, называемый <<, поэтому он успешно работает и сгребает символ в массив по указанному ключу.

Надеюсь, что это очистит.

+0

Нет, второй случай работает, потому что блок добавляет новый ключ в хэш. И '<<' никогда не используется в Hash в первом случае, он всегда используется в массиве. Блок не определяет тип возврата *, он определяет значение return *. –

+0

Фактически, в первом случае вы не получите '{}' в результате. Вы получаете '[]', потому что это то, что вы получаете в результате блока значений по умолчанию. Если вы * did * get '{}', то вызов '<<' на нем будет вызывать 'NoMethodError' вместо того, чтобы ничего не возвращать. –

3

В первом случае значение по умолчанию, возвращаемое, когда ключ не существует, равен []. Затем различные утверждения успешно добавляют различных собак и белок в возвращаемые массивы.

Однако ни в одной точке является ключевым когда-либо созданных для :dogs или :squirrels.

Во втором случае блок делает сохранить новое значение обратно в элемент хэша с помощью ключа.

Одна вещь, которая несколько интересна, - это то, как вы продолжаете получать новый пустой массив в первом случае. И ответ таков: вы не прошли [] как параметр, а как блок. Это исполняемый файл и сохраняется как proc. Каждый раз, когда ключ не найден, proc запускается снова и генерирует новый [].

Вы можете увидеть это в действии, обратите внимание на различные значения ID объекта:

irb > t = Hash.new { [] } 
=> {} 
irb > t[:a].object_id 
=> 2149202180 
irb > t[:a].object_id 
=> 2149192500