我經歷紅寶石koans,我有一個小麻煩了解此代碼將運行:傳遞給Hash.new的塊或對象何時創建或運行?
hash = Hash.new {|hash, key| hash[key] = [] }
如果在哈希任何值,什麼時候新的數組會被分配給一個給定的密鑰在哈希?第一次訪問哈希值時沒有首先分配哈希值是否會發生?請幫助我瞭解何時爲任何給定的散列鍵創建完全默認值。
我經歷紅寶石koans,我有一個小麻煩了解此代碼將運行:傳遞給Hash.new的塊或對象何時創建或運行?
hash = Hash.new {|hash, key| hash[key] = [] }
如果在哈希任何值,什麼時候新的數組會被分配給一個給定的密鑰在哈希?第一次訪問哈希值時沒有首先分配哈希值是否會發生?請幫助我瞭解何時爲任何給定的散列鍵創建完全默認值。
爲了讓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
不沒有鑰匙k
,h[k] #=> nil
,所以表達beco MES
(h[k] = nil || []) << v
成爲
(h[k] = []) << v
所以
h[k] #=> [v]
注意h[k]
上平等的左側使用方法Hash#[]=,而h[k]
在右邊採用Hash#[]。
該解決方案要求沒有散列值等於nil
。
第三溶液
的第三種方法是給散列的默認值。如果散列h
沒有一個關鍵k
,h[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
不具有關鍵k
,h[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"]}
免去了兩行代碼。
哪個最好?
就我個人而言,我對第二個和第三個解決方案漠不關心。兩者都在實踐中使用。
將新密鑰添加到散列時會調用該塊。在這種特定的情況下:
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
如果指定了一個塊,它將與哈希對象和重點調用,應返回默認值。如果需要,塊的責任是將值存儲在哈希中。
這是你有一個哈希其值全部陣列和你不希望每次檢查,看看是否哈希鍵已經存在和那個時代的語法糖在添加新元素之前,空數組已經被初始化。它允許這樣的:
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]
。這通常不是期望/預期的行爲。
提示:在您的塊中添加一個「puts」,當* *完全運行時,您將立即看到它。 –