Автовивизация рубинового хеша (грани)

Вот хитроумный трюк, чтобы включить автообнаружение хеша в рубине (взято из граней):

  # File lib/core/facets/hash/autonew.rb, line 19
  def self.autonew(*args)
    leet = lambda { |hsh, key| hsh[key] = new( &leet ) }
    new(*args,&leet)
  end

Хотя это работает (конечно), я нахожу это действительно расстраивающим, что я не могу понять, как это делают два лайнера, что он делает.

leet помещается как значение по умолчанию. Так что тогда просто доступ к h['new_key'] каким-то образом вызывает его и создает 'new_key' => {}

Теперь я ожидаю, что h['new_key'] вернет объект значения по умолчанию, а не его оценку. То есть 'new_key' => {} не создается автоматически. Итак, как на самом деле получать звонки? Особенно с двумя параметрами?

Ответ 1

Стандартный новый метод для Hash принимает блок. Этот блок вызывается в случае попытки доступа к ключу в Хэше, который не существует. Блок передается сам Хэш и запрошенный ключ (два параметра) и должен возвращать значение, которое должно быть возвращено для запрошенного ключа.

Вы заметите, что лямбда leet делает 2 вещи. Он возвращает новый хэш с leet как блок обработки значений по умолчанию. Это поведение, которое позволяет autonew работать для хэшей произвольной глубины. Он также присваивает этот новый хэш hsh[key], чтобы в следующий раз, когда вы запрашиваете тот же ключ, вы получите существующий Hash, а не новый.

Ответ 2

Также стоит отметить, что этот код можно сделать в однострочный слой следующим образом:

def self.autonew(*args)
  new(*args){|hsh, key| hsh[key] = Hash.new(&hsh.default_proc) }
end

Призыв к Hash # default_proc возвращает proc, который использовался для создания родителя, поэтому у нас есть хорошая рекурсивная настройка.

Я рассказываю об этом в этом случае в этом блоге.

Ответ 3

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

Если вы ищете то, чего еще не существует, вы получаете значение nil (или другое значение или исключение, если хотите), не создавая ничего, глядя. Он также может присоединяться к концу массивов.

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