2016-06-11 43 views
1
def unique(arr) 
    return arr.keep_if { |x| arr.count(x) == 1 } 
end 

print unique([2, 5, 5, 4, 22, 8, 2, 8]) 
#=> [4, 22, 2] 

值2在數組中出現兩次,但使用以下方法錯誤地返回它。爲什麼會發生這種情況,我該如何解決這個問題?爲什麼在Ruby中使用keep_if會跳過數組中的第一個元素?

+0

如果你是Matz,你將如何實現'Array#keep_if'? – Aetherus

+0

因爲在刪除第一個'2'後,只剩下一個'2',所以當你遇到第二個'2'時,條件是'true'。可變狀態再次發生。 –

+0

@JörgWMittag如果是這樣的話,爲什麼5和8不會返回? –

回答

4

不幸的是,這是由於keep_if工作方式中的一些隱藏行爲。爲了說明這個問題,我們可以利用掛最低的水果在我們的調試果園,好醇」 puts

def unique(arr) 
    return arr.keep_if { |x| 
    puts x, arr.join(',') 
    arr.count(x) == 1 
    } 
end 

print unique([2, 5, 5, 4, 22, 8, 2, 8]) 

這給了我們以下的輸出:

2 
2,5,5,4,22,8,2,8 
5 
2,5,5,4,22,8,2,8 
5 
2,5,5,4,22,8,2,8 
4 
2,5,5,4,22,8,2,8 
22 
4,5,5,4,22,8,2,8 
8 
4,22,5,4,22,8,2,8 
2 
4,22,5,4,22,8,2,8 
8 
4,22,2,4,22,8,2,8 
[4, 22, 2] 

仔細看確切地說,當方法發現一個新的值時會發生什麼:它將該值存儲在數組中的一個早期索引中,覆蓋已經存在的內容。下一次它找到一個想要保留的值時,它將它放在下一個點,等等。

這意味着第一次keep_if看起來2,它看到其中兩個,因此決定跳過它。但它看到它想要保留的4,並且會覆蓋第一個2。因此,第二次看到2時,它決定保留它。

+0

那麼爲什麼5不會保留,因爲其中的一個值被22所覆蓋? –

+1

,因爲直到_after_ 5被考慮之前它並沒有被誇大。 – Hamms

+0

@ChristopherByrd我相信這是因爲它已經在發生的時間裏通過了5秒。 –

1

您在迭代時正在更改數組。這是Matz自己的considered undefined behavior,如果你想避免像你在那裏看到的奇怪行爲,你應該避免這樣做。

而不是你keep_if方法,你應該使用這樣的:

arr.select{ |x| arr.count(x) == 1 } 

這不會更改到位數組但卻會返回一個新的。

+0

你的第一句話太強大了。 'arr = [1,2,3]; arr.map! {| n | 2 * n}'在你遍歷它時改變'arr',但它不是未定義的行爲。我們通常知道你的意思 - 從數組中刪除元素(或將元素插入到數組中),但你應該更精確。 –

+0

實際上,在這種情況下,當你改變它的時候('count'),你正在迭代它('keep_if');-)這就是爲什麼這個問題比改變數組這個有點衆所周知的問題更加微妙同時迭代。 –

相關問題