Я думал, что понял, что метод по умолчанию делает с хешем ...
Задайте значение по умолчанию для ключа, если он не существует:
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}
Все хорошо.
Но если я установил по умолчанию пустой список или пустой хеш, я не понимаю его поведения в все ....
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
Кто-нибудь может объяснить, что происходит?

Hash.default используется для установки значения по умолчанию вернулся, когда вы запрашиваете ключ, который не существует. Запись в коллекции не создается для вас только потому, что ее запросили.
Кроме того, значение, которое вы установили для default, является экземпляром объекта (в вашем случае - массивом), поэтому, когда он возвращается, им можно управлять.
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
Я хотел бы отметить, что это поведение отличается от defaultdict Python, где аналогичный код работает нормально.
Ух ты, это ужасно и ужасно подвержено ошибкам из-за динамического изменения этого значения по умолчанию. Я просто потратил 3 часа, пытаясь понять, что происходит при попытке установить по умолчанию []. ИМНШО, дефолт не должен так выставляться. Альтернатива (h = Hash.new {| h, k | h [k] = []}) на самом деле не делает того, что я хочу, потому что она всегда создает новый массив, просто ссылаясь на хеш-ключ. Теперь я вернулся к тому, чего пытался избежать в первую очередь: h [k] = [], если только h.has_key? (K) :)
Надеюсь, вы, ребята, не проголосовали против этого ответа только из-за этой особенности самого Ruby :) Что не так с этим прекрасно объяснительным ответом? Я сам не полностью разобрался с концепцией, пока не увидел акцент на «возвращенном» и объяснение важности того, что значение default является объектом (чтобы оно сохранялось при последующих вызовах несуществующих клавиш).
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
Это очень полезная идиома:
(myhash[key] ||= []) << value
Он даже может быть вложенным:
((myhash[key1] ||= {})[key2] ||= []) << value
Другой способ - сделать:
myhash = Hash.new {|hash,key| hash[key] = []}
Но у этого есть значительный побочный эффект, заключающийся в том, что спрашивая о ключе создаст его, который отображает has_key? довольно бесполезно, поэтому я избегаю этого метода.
Я не думаю, что побочный эффект последней техники присутствует в Ruby 1.9.2. myhash = Hash.new {| хеш, ключ | хэш [ключ] = []}; myhash.has_key? (: test) # => ложь
О, нет, я имел в виду, что puts myhash[:test] или что-то подобное, которое кажется безвредным, теперь приведет к тому, что myhash.has_key?(:test) будет верным.
Я не уверен, что это то, что вы хотите, но вы можете сделать это, чтобы всегда возвращать пустой массив при запросе отсутствующего хеш-ключа.
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]
=> []
Я думаю, что это именно то поведение, которое вы ищете. Это автоматически инициализирует любые новые ключи в хэше массивом:
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=>[]}
Гленн Макдональд говорит:
"Другой способ:
myhash = Hash.new {| хеш, ключ | хэш [ключ] = []}
Но у этого есть существенный побочный эффект: запрос о ключе создаст его, который отображает 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? не.
Если вы действительно хотите иметь бесконечно глубокий хеш:
endless = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
endless["deep"]["in"]["here"] = "hello"
Конечно, как указывает Гленн выше, если вы это сделаете, has_key? теряет смысл, так как всегда будет возвращать истину. Спасибо jbarnette за это.
хорошее объяснение, имеет смысл