2008-10-10 12 views
39

Ich dachte, ich verstanden, was die Standardmethode auf einen Hash tut ...Rubin Hash .default auf eine Liste Einstellung

einen Standardwert für einen Schlüssel geben, wenn es nicht existiert:

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} 

Alles gut.

Aber wenn ich die Standardeinstellung eine leere Liste oder leere Hash sein, verstehe ich nicht, es ist das Verhalten bei alle ....

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 [] ?? 

Ich hatte gehofft,/erwarten das gleiche Verhalten als ob ich den || = Operator verwendet hätte ...

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 

Kann mir jemand erklären, was vor sich geht?

Antwort

48

Hash.default wird verwendet, um den Standardwert zurückgegeben, wenn Sie einen Schlüssel abfragen, der nicht existiert. Ein Eintrag in der Sammlung wird nicht für Sie erstellt, nur weil er abgefragt wurde.

Auch der Wert, den Sie default setzen, ist eine Instanz eines Objekts (in Ihrem Fall ein Array). Wenn dies zurückgegeben wird, kann es manipuliert werden.

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 
+1

gute Erklärung, macht Sinn –

+1

Ich möchte darauf hinweisen, dass dieses Verhalten unterscheidet sich von Python defaultdict, wo der analoge Code funktioniert gut. –

+2

Wow, das ist schrecklich und furchtbar anfällig für Bugs, indem dieser Standardwert dynamisch geändert wird. Ich habe gerade 3 Stunden damit verbracht herauszufinden, ob wtf lief, als ich versuchte, den Standard auf [] zu setzen. IMNSHO, Standard sollte nicht so angezeigt werden. Die Alternative (h = Hash.neu {| h, k | h [k] = []} tut nicht wirklich das, was ich will, weil das immer ein neues Array erzeugt, indem man nur den Hash-Schlüssel referenziert. Jetzt bin ich wieder dabei, das zu tun, was ich eigentlich vermeiden wollte: h [k] = [] es sei denn h.has_key? (K) :) –

6
irb(main):002:0> a.default = [] 
=> [] 
irb(main):003:0> a[8] << 9 
=> [9]       # great! 

Mit dieser Aussage haben Sie den Standard geändert; Sie haben kein neues Array erstellt und "9" hinzugefügt. An diesem Punkt ist es identisch, wenn Sie dies stattdessen getan hatte:

irb(main):002:0> a.default = [9] 
=> [9] 

Daher ist es keine Überraschung, dass Sie jetzt diese:

irb(main):006:0> a[9] 
=> [9]       # unawesome! shouldn't this be [] ?? 

Darüber hinaus ist die ‚< <‘ fügte die ‚9‘ zum Array; es hat fügen Sie es nicht mit dem Hash, der diese erklärt:

irb(main):004:0> a 
=> {}       # ?! would have expected {8=>[9]} 

Statt mit .default, was Sie wahrscheinlich in Ihrem Programm tun möchten, ist dies so etwas wie:

# Time to add a new entry to the hash table; this might be 
# the first entry for this key.. 
myhash[key] ||= [] 
myhash[key] << value 
51

Dies ist ein sehr nützlich Idiom:

(myhash[key] ||= []) << value 

Es kann sogar verschachtelt werden:

((myhash[key1] ||= {})[key2] ||= []) << value 

Der andere Weg ist zu tun:

myhash = Hash.new {|hash,key| hash[key] = []} 

Aber dies hat die wesentliche Nebeneffekte, dass über einen Schlüssel gefragt wird es schaffen, die has_key macht? ziemlich nutzlos, also vermeide ich diese Methode.

+5

Ich denke nicht sicher, dass der Nebeneffekt der letzten Technik in Ruby 1.9.2 vorhanden ist. myhash = Hash.new {| hash, Schlüssel | Hash [Schlüssel] = []}; myhash.has_key? (: test) # => false –

+3

Oh, nein, ich meinte, dass 'puts myhash [: test]' oder ähnliches, was scheinbar harmlos sein sollte, nun zu 'myhash.has_key? (: test) 'ist danach wahr. –

-4

Ich bin mir nicht sicher, ob dies das ist, was Sie wollen, aber Sie können immer ein leeres Array zurückgeben, wenn ein fehlender Hash-Schlüssel abgefragt wird.

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] 
    => [] 
32

Ich denke, das ist das Verhalten, das Sie suchen. Dies wird initialisiert automatisch alle neuen Schlüssel in der Hash in ein Array:

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=>[]} 
9

Glenn McDonald sagt:

„Die andere Art und Weise zu tun ist:

myhash = Hash.new {| Hash, key | hash [key] = []}

Aber dies hat den signifikanten Nebeneffekt, dass die Frage nach einem Schlüssel es erzeugt, was has_key? ziemlich nutzlos macht, also vermeide ich diese Methode. "

das scheint tatsächlich nicht wahr zu sein.

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 

Zugriff der Schlüssel wird es schaffen, als ich erwarten würde. Einfach nach has_key fragen? nicht.

9

Wenn Sie wirklich wollen, ein endlos tief Hash haben:

endless = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) } 
endless["deep"]["in"]["here"] = "hello" 

Natürlich, wie Glenn oben weist darauf hin, wenn Sie dies tun, die has_key? verliert seine Bedeutung, da es immer wieder wahr wird. Danke an Jbarnette für diesen.

Verwandte Themen