Pergunta

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?

Foi útil?

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]
   => []
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top