2016-11-24 46 views
2

我經歷紅寶石koans,我有一個小麻煩了解此代碼將運行:傳遞給Hash.new的塊或對象何時創建或運行?

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

如果在哈希任何值,什麼時候新的數組會被分配給一個給定的密鑰在哈希?第一次訪問哈希值時沒有首先分配哈希值是否會發生?請幫助我瞭解何時爲任何給定的散列鍵創建完全默認值。

+0

提示:在您的塊中添加一個「puts」,當* *完全運行時,您將立即看到它。 –

回答

1

爲了讓Ruby的新手們受益,我已經討論瞭解決這個問題的其他方法,包括這個問題的實質內容。

任務

假設你將得到一個數組

arr = [[:dog, "fido"], [:car, "audi"], [:cat, "lucy"], [:dog, "diva"], [:cat, "bo"]] 

,並希望創建哈希

{ :dog=>["fido", "diva"], :car=>["audi"], :cat=>["lucy", "bo"] } 

首先解決

h = {} 
arr.each do |k,v| 
    h[k] = [] unless h.key?(k) 
    h[k] << v 
end 
h #=> {:dog=>["fido", "diva"], :car=>["audi"], :cat=>["lucy", "bo"]} 

這是非常簡單的。

解決方法二

更多紅寶石般就是寫:

h = {} 
arr.each { |k,v| (h[k] ||= []) << v } 
h #=> {:dog=>["fido", "diva"], :car=>["audi"], :cat=>["lucy", "bo"]} 

當紅寶石看到(h[k] ||= []) << v她做的第一件事就是它擴大到

(h[k] = h[k] || []) << v 

如果h不沒有鑰匙kh[k] #=> nil,所以表達beco MES

(h[k] = nil || []) << v 

成爲

(h[k] = []) << v 

所以

h[k] #=> [v] 

注意h[k]上平等的左側使用方法Hash#[]=,而h[k]在右邊採用Hash#[]

該解決方案要求沒有散列值等於nil

第三溶液

的第三種方法是給散列的默認值。如果散列h沒有一個關鍵kh[k]返回默認值。有兩種類型的默認值。

如果空數組被作爲參數傳遞到Hash::new傳遞默認值作爲參數傳遞給Hash::new

,該值將成爲默認值:

a = [] 
a.object_id 
    #=> 70339916855860 
g = Hash.new(a) 
    #=> {} 

g[k]返回[]h沒有鑰匙k。 (然而,哈希沒有改變。)這個構造具有重要的用途,但在這裏不合適。要知道爲什麼,假設我們寫

x = g[:cat] << "bo" 
    #=> ["bo"] 
y = g[:dog] << "diva" 
    #=> ["bo", "diva"] 
x #=> ["bo", "diva"] 

這是因爲:cat:dog值都設置爲同一個對象,一個空數組。我們可以通過檢查object_id看看這樣:

x.object_id 
    #=> 70339916855860 
y.object_id 
    #=> 70339916855860 

Hash::new給予它返回默認值

默認值的第二種形式是執行塊運算的塊。如果我們用一個塊定義的散列:

h = Hash.new { |h,k| h[key] = [] } 

然後如果h不具有關鍵kh[k]將被設置爲等於由塊返回,在此情況下爲空數組的值。請注意,塊變量h是新創建的空散列。因此,這使我們能夠寫出

h = Hash.new { |h,k| h[k] = [] } 
arr.each { |k,v| h[k] << v } 
h #=> {:dog=>["fido", "diva"], :car=>["audi"], :cat=>["lucy", "bo"]} 

作爲傳遞到塊中的第一個元素是arr.first,塊變量通過評估

k, v = arr.first 
    #=> [:dog, "fido"] 
k #=> :dog 
v #=> "fido" 

塊計算分配的值是

h[k] << v 
    #=> h[:dog] << "fido" 

但由於h尚未(有)鑰匙:dog,該塊被觸發,設置爲h[k] equ人到[],然後將該空數組附加有「汪汪」,使

h #=> { :dog=>["fido"] } 

類似地,後的arr接下來的兩個元件被傳遞到塊中,我們有

h #=> { :dog=>["fido"], :car=>["audi"], :cat=>["lucy"] } 

當下一個的arr(第四)元素傳遞到塊,我們評估

h[:dog] << "diva" 

但現在h確實有一個關鍵,所以德發ult不適用,我們以

h #=> {:dog=>["fido", "diva"], :car=>["audi"], :cat=>["lucy"]} 

arr的最後一個元素進行類似的處理。

需要注意的是,使用Hash ::新與塊的時候,我們可以寫這樣的事:

h = Hash.new { launch_missiles("any time now") } 

在這種情況下h[k]將被設定爲等於launch_missiles返回值。換句話說,任何事情都可以在該塊中完成。

更紅寶石般

最後,寫

h = Hash.new { |h,k| h[k] = [] } 
arr.each { |k,v| h[k] << v } 
h #=> {:dog=>["fido", "diva"], :car=>["audi"], :cat=>["lucy", "bo"]} 

的多個紅寶石般的方法是使用Enumerable#each_with_object

arr.each_with_object(Hash.new { |h,k| h[k] = [] }) { |k,v| h[k] << v } 
    #=> {:dog=>["fido", "diva"], :car=>["audi"], :cat=>["lucy", "bo"]} 

免去了兩行代碼。

哪個最好?

就我個人而言,我對第二個和第三個解決方案漠不關心。兩者都在實踐中使用。

1

將新密鑰添加到散列時會調用該塊。在這種特定的情況下:

hash["d"] #calls the block and store [] as a value of "d" key 
hash["d"] #should print [] 

欲瞭解更多信息,請訪問:https://docs.ruby-lang.org/en/2.0.0/Hash.html

如果指定了一個塊,它將與哈希對象和重點調用,應返回默認值。如果需要,塊的責任是將值存儲在哈希中。

0

使生活更輕鬆

這是你有一個哈希其值全部陣列和你不希望每次檢查,看看是否哈希鍵已經存在和那個時代的語法糖在添加新元素之前,空數組已經被初始化。它允許這樣的:

hash[:new_key] << new_element

,而不是這樣的:

hash[:new_key] = [] unless hash[:new_key] 
hash[:new_key] << new_element 

解決了一個老問題

這也是指定的哈希值的默認值的更簡單的方法,它看起來替代像這樣:

hash = Hash.new([])

這種方法的問題是相同的數組對象被用作所有鍵的默認值。所以

hash = Hash.new([]) 
hash[:a] << 1 
hash[:b] << 2 

將返回[1, 2]對於任何hash[:a]hash[:b],或爲此事甚至hash[:foo]。這通常不是期望/預期的行爲。

相關問題