Definir rubi de hash .default a uma lista [duplicado]
Pergunta
Esta questão já tem uma resposta aqui:
Eu pensei que eu entendi o que o método padrão faz para um hash ...
Dê um valor padrão para a chave se ela não existe:
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}
Tudo de bom.
Mas se eu definir o padrão a ser uma lista vazia, ou de hash vazio, eu não entendo o comportamento é no todas ....
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 [] ??
Eu estava esperando / esperando o mesmo comportamento como se eu tivesse usado o || = operador ...
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
Alguém pode explicar o que está acontecendo?
Solução
Hash.default
é usado para definir o valor padrão retornou quando você consulta uma chave que não existe. Uma entrada na coleção não é criado para você, só porque interrogado.
Além disso, o valor definido default
a é uma instância de um objeto (um Array no seu caso), então quando este é devolvido, ele pode ser manipulado.
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
Outras dicas
Este é um idioma muito útil:
(myhash[key] ||= []) << value
Ele pode até mesmo ser aninhados:
((myhash[key1] ||= {})[key2] ||= []) << value
A outra maneira é fazer:
myhash = Hash.new {|hash,key| hash[key] = []}
Mas isso tem o efeito colateral significativo que pedindo sobre uma chave irá criá-la, o que torna has_key? bastante inútil, então eu evitar este método.
Eu acho que este é o comportamento que você está procurando. Isto irá inicializar automaticamente quaisquer novas chaves no Hash para uma matriz:
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=>[]}
Glenn McDonald diz:
"A outra maneira é fazer:
myhash = Hash.new {| de hash, chave | Hash [key] = []}
Mas isso tem o efeito colateral significativo que perguntando sobre uma chave irá criá-la, o que torna has_key? bastante inútil, então eu evitar este método. "
que de fato não parece ser verdade.
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
Acessando a chave irá criá-lo, como seria de esperar. Apenas pedindo has_key? não.
Se você realmente quer ter um hash infinitamente profunda:
endless = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
endless["deep"]["in"]["here"] = "hello"
Claro que, como aponta Glenn acima, se você fizer isso, o has_key? perde o seu significado como sempre retornará verdadeiro. Thx para jbarnette para este.
irb(main):002:0> a.default = []
=> []
irb(main):003:0> a[8] << 9
=> [9] # great!
Com esta declaração, você modificou o padrão; você não tiver criado uma nova matriz e acrescentou "9". Neste ponto, é idêntico ao se você tivesse feito isso em vez disso:
irb(main):002:0> a.default = [9]
=> [9]
Por isso não é nenhuma surpresa que agora você pode obter o seguinte:
irb(main):006:0> a[9]
=> [9] # unawesome! shouldn't this be [] ??
Além disso, o '<<' adicionado a '9' para a matriz; não adicioná-lo para o hash, o que explica o seguinte:
irb(main):004:0> a
=> {} # ?! would have expected {8=>[9]}
Em vez de usar .default, o que você provavelmente vai querer fazer em seu programa é algo como isto:
# Time to add a new entry to the hash table; this might be
# the first entry for this key..
myhash[key] ||= []
myhash[key] << value
Eu não tenho certeza se é isso que você quer, mas você pode fazer isso para sempre retornar um array vazio quando uma chave de hash faltando é consultado.
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]
=> []