2012-11-11 92 views
2

可能重複:
Ruby method Array#<< not updating the array in hash
Strange ruby behavior when using Hash.new([])Hash.new([])並不像預期的那樣

我一直在做Koans這是偉大的,當我去我發現沒有什麼大麻煩,但是我偶然發現了這一點,並且無法理解它:

def test_default_value_is_the_same_object 
     hash = Hash.new([]) 

     hash[:one] << "uno" 
     hash[:two] << "dos" 

     assert_equal ["uno", "dos"], hash[:one] # But I only put "uno" for this key! 
     assert_equal ["uno", "dos"], hash[:two] # But I only put "dos" for this key! 
     assert_equal ["uno", "dos"], hash[:three] # I didn't shove anything for :three! 

     assert_equal true, hash[:one].object_id == hash[:two].object_id 
    end 

所有的測試都通過了(我只是看着錯誤,這有助於我猜測正確的寫法)。

最後一個斷言,好吧,它們都沒有初始化,所以它們的值必須具有相同的對象ID,因爲它們都採用默認值。

我不明白爲什麼默認值被改變了,我甚至不完全確定發生了什麼。

我在IRB中試過了,也許在Hash/Array上有些篡改是爲了讓我瘋狂,但我得到了同樣的結果。

我首先想到hash[:one] << "uno"意味着hash變成{ one: ["uno] },但它仍然是{ }
雖然我猜<<只要求push,當你使用=標誌

請告訴我,我錯過了新的密鑰只添加。

編輯:我使用Ruby 1.9.3

+1

只有*一個*數組:在* new'方法被調用之前,值('[]')被計算*。嘗試使用塊的「默認形式」(每次創建/調用塊時創建一個* new *數組)。我相信這是一個重複.. – 2012-11-11 23:03:40

+0

是的,被這個幾次被咬了:) –

+0

@pst你能詳細說明嗎?我不確定我在追隨。我沒有在SO/Google/Duckduckgo中發現任何類似的問題,但我總是會錯過任何東西;) –

回答

3

當您使用哈希的默認參數時,所有未明確設置的鍵都使用同一個對象。這意味着只有一個陣列在這裏被使用,你傳遞給Hash.new的那個。請參閱下面的證據。

>> h = Hash.new([]) 
=> {} 
>> h[:foo] << :bar 
=> [:bar] 
>> h[:bar] << :baz 
=> [:bar, :baz] 
>> h[:foo].object_id 
=> 2177177000 
>> h[:bar].object_id 
=> 2177177000 

奇怪的是,當你發現,如果你檢查哈希,你會發現它是空的!這是因爲只有默認對象已被修改,尚未分配任何密鑰。

幸運的是,還有另一種做哈希默認值的方法。您可以提供一個默認的塊來代替:

>> h = Hash.new { |h,k| h[k] = [] } 
=> {} 
>> h[:foo] << :bar 
=> [:bar] 
>> h[:bar] << :baz 
=> [:baz] 
>> h[:foo].object_id 
=> 2176949560 
>> h[:bar].object_id 
=> 2176921940 

當您使用此方法,該塊被每次尚未分配的按鍵是用來執行,它提供了哈希本身和密鑰作爲參數。通過在塊內分配默認值,可以確保爲每個不同的鍵創建一個新對象,並且分配將自動發生。這是在Ruby中創建「Hash of Arrays」的慣用方法,通常比默認參數方法更安全。這就是說,如果你使用不可變的值(比如數字),像Hash.new(0)這樣的工作是安全的,因爲你只能通過重新賦值來改變這些值。但是因爲我更喜歡在我的腦海中保留更少的概念,所以我幾乎全部使用了塊形式。

1

當你

h = Hash.new(0) 
h[:foo] += 1 

您直接修改hh[:foo] += 1h[:foo] = h[:foo]+1相同。 h[:foo]正在分配0+1

當你

h = Hash.new([]) 
h[:foo] << :bar 

要修改h[:foo]這是[],這是h默認值,但不是價值的h任意鍵。之後,[]變爲[:bar],默認值h變爲[:bar],但這不是h[:foo]的值。

相關問題