2008-10-10 62 views
39

我想我明白了默認的方法做一個哈希...紅寶石哈希.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} 

一切都好。

但如果我設置的默認是一個空列表,或空哈希,我不知道這是在所有 ....

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 

任何人都可以解釋發生了什麼事嗎?

回答

48

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

很好的解釋,有道理 – 2008-10-10 11:01:09

+1

我想指出,這種行爲是不同於python的defaultdict,其中類似的代碼工作得很好。 – 2012-10-10 07:57:03

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 
51

這是一個非常有用的成語:

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

它甚至可以被嵌套:

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

另一種方式是做:

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

但這有顯著副作用,即詢問約一鍵將創建它,這使得對象的has_key?相當無用,所以我避免了這種方法。

-4

我不確定這是否是你想要的,但是你可以這樣做,當查詢丟失的散列鍵時總是返回一個空數組。

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

我認爲這是您正在尋找的行爲。這將自動初始化哈希任何新鍵的數組:

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

格倫·麥克唐納說:

「的另一種方法是做:

myhash = Hash.new {|哈希, 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?纔不是。

9

如果你真的想有一個深不休哈希:

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

當然,正如上面格倫指出,如果你做到這一點,對象的has_key?它的意義會失去它的意義,因爲它總會迴歸真實。 Thx to jbarnette爲這一個。