Установка рубинового хэша .default в список
Я думал, что понял, что метод по умолчанию делает с хешем...
Дайте значение по умолчанию для ключа, если оно не существует:
irb(main):001:0> a = {}
=> {}
irb(main):002:0> a.default = 4
=> 4
irb(main):003:0> a[8]
=> 4
irb(main):004:0> a[9] += 1
=> 5
irb(main):005:0> a
=> {9=>5}
Все хорошо.
Но если я задал по умолчанию пустой список или пустой хеш, я не понимаю его поведения в all....
irb(main):001:0> a = {}
=> {}
irb(main):002:0> a.default = []
=> []
irb(main):003:0> a[8] << 9
=> [9] # great!
irb(main):004:0> a
=> {} # ?! would have expected {8=>[9]}
irb(main):005:0> a[8]
=> [9] # awesome!
irb(main):006:0> a[9]
=> [9] # unawesome! shouldn't this be [] ??
Я надеялся/ожидал такого же поведения, как если бы я использовал оператор || =...
irb(main):001:0> a = {}
=> {}
irb(main):002:0> a[8] ||= []
=> []
irb(main):003:0> a[8] << 9
=> [9]
irb(main):004:0> a
=> {8=>[9]}
irb(main):005:0> a[9]
=> nil
Кто-нибудь может объяснить, что происходит?
Ответы
Ответ 1
Hash.default
используется для установки значения по умолчанию возвращено при запросе ключа, который не существует. Запись в коллекции не создана для вас, просто потому, что она запросила его.
Кроме того, значение, которое вы установили default
to, является экземпляром объекта (массив в вашем случае), поэтому, когда это возвращается, его можно манипулировать.
a = {}
a.default = [] # set default to a new empty Array
a[8] << 9 # a[8] doesn't exist, so the Array instance is returned, and 9 appended to it
a.default # => [9]
a[9] # a[9] doesn't exist, so default is returned
Ответ 2
Это очень полезная идиома:
(myhash[key] ||= []) << value
Он может быть даже вложенным:
((myhash[key1] ||= {})[key2] ||= []) << value
Другой способ:
myhash = Hash.new {|hash,key| hash[key] = []}
Но это имеет существенный побочный эффект, который запрашивает о ключе, создаст его, что делает has_key? бесполезно, поэтому я избегаю этого метода.
Ответ 3
Я думаю, что это то поведение, которое вы ищете. Это автоматически инициализирует любые новые ключи в Hash для массива:
irb(main):001:0> h = Hash.new{|h, k| h[k] = []}
=> {}
irb(main):002:0> h[1] << "ABC"
=> ["ABC"]
irb(main):003:0> h[3]
=> []
irb(main):004:0> h
=> {1=>["ABC"], 3=>[]}
Ответ 4
glenn mcdonald говорит:
"Другой способ:
myhash = Hash.new {| hash, key | hash [key] = []}
Но у этого есть существенный побочный эффект, который задает вопрос о ключе, создаст его, который отображает has_key? довольно бесполезно, поэтому я избегаю этого метода ".
на самом деле это не так.
irb(main):004:0> a = Hash.new {|hash,key| hash[key] = []}
=> {}
irb(main):005:0> a.has_key?(:key)
=> false
irb(main):006:0> a[:key]
=> []
irb(main):007:0> a.has_key?(:key)
=> true
Доступ к ключу создаст его, как и следовало ожидать. Просто спрашивать has_key? не.
Ответ 5
Если вы действительно хотите иметь бесконечный глубокий хеш:
endless = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
endless["deep"]["in"]["here"] = "hello"
Конечно, как указывает Гленн выше, если вы это сделаете, has_key? теряет свой смысл, поскольку он всегда будет возвращать истину. спасибо для jbarnette для этого.
Ответ 6
irb(main):002:0> a.default = []
=> []
irb(main):003:0> a[8] << 9
=> [9] # great!
С помощью этого утверждения вы изменили значение по умолчанию; вы не создали новый массив и добавили "9". На данный момент это идентично, если вы сделали это вместо этого:
irb(main):002:0> a.default = [9]
=> [9]
Поэтому неудивительно, что теперь вы получаете это:
irb(main):006:0> a[9]
=> [9] # unawesome! shouldn't this be [] ??
Кроме того, '< < добавили "9" в массив; это не добавило его в хэш, что объясняет это:
irb(main):004:0> a
=> {} # ?! would have expected {8=>[9]}
Вместо использования .default то, что вы, вероятно, хотите сделать в вашей программе, выглядит примерно так:
# Time to add a new entry to the hash table; this might be
# the first entry for this key..
myhash[key] ||= []
myhash[key] << value
Ответ 7
Я не уверен, что это то, что вы хотите, но вы можете сделать это, чтобы всегда возвращать пустой массив, когда запрашивается отсутствующий хэш-ключ.
h = Hash.new { [] }
h[:missing]
=> []
#But, you should never modify the empty array because it isn't stored anywhere
#A new, empty array is returned every time
h[:missing] << 'entry'
h[:missing]
=> []