вставка элементов в массив в рубиновом хеше - PullRequest
9 голосов
/ 15 февраля 2011

В Ruby, чтобы создать хэш массивов и поместить элементы в эти массивы, я видел две идиомы. Я хотел бы знать, кто из них предпочитает и почему. (Раскрытие: у меня есть собственное мнение, но я хочу убедиться, что я не упускаю ничего очевидного.)

Подход 1: используйте необычный инициализатор Хэша:

ht = Hash.new {|h,k| h[k]=[]}
ht["cats"] << "Jellicle"
ht["cats"] << "Mr. Mistoffelees"

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

Подход 2: простой инициализатор, модный аксессуар:

ht = {}
(ht["cats"] ||= []) << "Jellicle"
(ht["cats"] ||= []) << "Mr. Mistoffelees"

Есть ли у людей мнение о том, какое из них лучше (или ситуации, когда одно предпочтительнее другого)?

Ответы [ 4 ]

18 голосов
/ 16 февраля 2011

Иногда хеш изначально заполняется данными, а позже он используется только для извлечения данных.В этих случаях я предпочитаю первую возможность, потому что процедура по умолчанию может быть «очищена» (в Ruby 1.9).

ht = Hash.new {|h,k| h[k]=[]}
ht["cats"] << "Jellicle"
ht["cats"] << "Mr. Mistoffelees"
ht["dogs"]
p ht
#=> {"cats"=>["Jellicle", "Mr. Mistoffelees"], "dogs"=>[]}

ht.default_proc = proc{}
ht["parrots"] #nil
p ht
#=> {"cats"=>["Jellicle", "Mr. Mistoffelees"], "dogs"=>[]} No parrots!
2 голосов
/ 16 февраля 2011

В ОП я сказал, что у меня свое мнение. Вот оно.

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

Рассмотрим следующее:

>> ht1 = Hash.new {|h,k| h[k]=[]}
>> ht2 = {}
>> ht1["cats"] << "Jellicle"
=> ["Jellicle"]
>> (ht2["cats"] ||= []) << "Jellicle"
=> ["Jellicle"]

пока все хорошо - ht1 и ht2 идентичны. но:

>> ht1["dogs"] ? "got dogs" : "no dogs"
=> "got dogs"
>> ht2["dogs"] ? "got dogs" : "no dogs"
=> "no dogs"

Обратите внимание, что простой доступ к ht1 [some_key] изменяет состояние хеш-таблицы , т. Е. Создает новую запись. Вы можете утверждать, что конечный пользователь всегда должен использовать has_key? () Для проверки наличия хеш-записи - и вы были бы правы - но приведенное выше использование является принятой идиомой. Автоматическое создание записи в хеш-таблице было бы неожиданным побочным эффектом, поэтому следует соблюдать осторожность, если хеш-таблица когда-либо будет доступна конечному пользователю. (Обратите внимание, однако, что ответ Стинслага показывает, как вы можете отключить это.)

2 голосов
/ 15 февраля 2011

Если вы заранее знаете номер и имя для каждой клавиши, тогда вы можете использовать первый вариант. Или даже более простой

ht = { "cats" => [] }

В противном случае, если вы не хотите (нужно) предварительно инициализировать хеш, второй вариант - хороший выбор.

0 голосов
/ 15 февраля 2011

Лично я предпочитаю:

ht = Hash.new {|h,k| h[k]=[]}
ht["cats"] << "Jellicle"
ht["cats"] << "Mr. Mistoffelees"

Это вопрос технического обслуживания и самодокументирования для меня.Грязная инициализация хэша происходит один раз.С этого момента все присвоения массивам в хэше выполняются стандартным образом, в результате чего ваш код выглядит нормально.

Альтернативный метод:

ht = {}
(ht["cats"] ||= []) << "Jellicle"
(ht["cats"] ||= []) << "Mr. Mistoffelees"

начинает выглядеть нормально, но у каждого назначения есть то, что я называю "визуальный шум".Мы должны мысленно декодировать, что означает ||= [] каждый раз, когда что-то помещается в массив в хэше.Это утомляет умственно, и если вы делаете это по всему коду, это приведет к менее элегантно выглядящему коду и постоянной переоценке того, что, черт возьми, код делает в ТО месте.

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