2010-06-09 50 views
10
k = [1,2,3,4,5] 
for n in k 
    puts n 
    if n == 2 
    k.delete(n) 
    end 
end 
puts k.join(",") 

# Result: 
# 1 
# 2 
# 4 
# 5 
# [1,3,4,5] 

# Desired: 
# 1 
# 2 
# 3 
# 4 
# 5 
# [1,3,4,5] 

此相同的效果會發生與其他陣列迭代器,k.each:乾淨的解決這個ruby迭代器的技巧?

k = [1,2,3,4,5] 
k.each do |n| 
    puts n 
    if n == 2 
    k.delete(n) 
    end 
end 
puts k.join(",") 

具有相同的輸出。

這種情況發生的原因很明顯...... Ruby實際上並不是遍歷存儲在數組中的對象,而是將其轉換爲漂亮的數組索引迭代器,從索引0開始,每次增加索引,直到它結束。但是當你刪除一個項目時,它仍然會增加索引,所以它不會評估兩次相同的索引,這是我想要的。

可能不是發生了什麼,但它是我能想到的最好的。

有沒有一個乾淨的方法來做到這一點?是否已經有一個內置的迭代器可以做到這一點?或者我將不得不將它弄髒並做一個數組索引迭代器,並且在項目被刪除時不增加? (或者通過陣列的克隆迭代,並從原來的數組中刪除)


澄清

我不只是想刪除一個數組的項目;對不起,如果這是明確的。我想要做的是遍歷每個元素,並「處理」它;這個過程有時可能會刪除它。爲了更加準確:

class Living_Thing 

    def initialize tracker,id 
    @tracker = tracker 
    @id = id 

    @tracker << self 
    end 

    def process 
    do_stuff 
    puts @id 
    if @id == 2 
     die 
    end 
    end 

    def die 
    do_stuff_to_die 
    @tracker.delete(self) 
    end 

    def inspect 
    @id 
    end 
end 

tracking_array = Array.new() 

foo = Living_Thing.new(tracking_array,1) 
bar = Living_Thing.new(tracking_array,2) 
rab = Living_Thing.new(tracking_array,3) 
oof = Living_Thing.new(tracking_array,4) 

puts tracking_array.join(",")    # => [1, 2, 3, 4] 

for n in tracking_array 
    n.process 
end 

# result: only foo, bar, and oof are processed 

理想情況下,我希望處理tracking_array中的所有項目。

當Living_Thing從tracking_array中移除時,Living_Thing#死亡必須調用; do_stuff_to_die清理必須註冊的事物。

+0

這是爲Rails應用程序或只是Ruby? – Anurag 2010-06-09 04:42:38

+0

這是純粹的紅寶石 – 2010-06-09 04:53:01

回答

4

這可能更適合處理(參考更新說明)

k = [1,2,3,4,5] 
k.dup.each do |n| 
    puts n 
    if n == 2 
    k.delete(n) 
    end 
end 
puts k.join(",") 

它旁邊的步驟,雖然你已經(關於通過對象迭代通過指數迭代)

+0

謝謝;我沒有想過在一行中使用k.dup和.each,而不是使用temp變量;這是我現在要使用的解決方案...雖然,是的,它確實可以避免實際的迭代刪除問題。 – 2010-06-09 04:33:46

3

好了,讓我們說你要消除所有2的與陣列:

arr = [1,2,3,4,5] 
arr.delete(2) 
puts arr.join(", ") 
# => "1, 3, 4, 5" 
arr = [1,2,3,2,4,2,5,2] 
arr.delete(2) 
puts arr.join(", ") 
# => "1, 3, 4, 5" 

但我懷疑你想要遍歷,所以我想:

arr = [1,2,3,4,5] 
arr.each {|x| a[a.index(x)] = nil if x == 2}.compact! 

也許這就是太髒?對nil的賦值保持了迭代器的計數正確,並且在事實之後,compact!消除了nils。當然map保持它有點短,更乾淨:

arr.map {|x| x if x != 2}.compact! 
15

這是一個在大多數語言錯誤發生變異的集合,而你迭代它。在大多數語言中,解決方案是創建一個副本並進行變更,或者在迭代完成時創建索引列表並對這些索引執行操作,但是Ruby的迭代器給了您更多的信息。一些解決方案是顯而易見的。最慣用IMO:

k.delete_if do |n| 
    puts n 
    n == 2 
end 
puts k.join(',') 

delete_if基本上是reject破壞性版本,它返回對象的數組,該塊沒有返回true:

puts k 
puts k.reject {|n| n == 2}.join(',') 

更直接地從你的榜樣翻譯)

+0

不錯,我喜歡delete_if非常乾淨的解決方案 – 2010-06-09 03:59:05

+0

使用delete_if爲每個項目運行完整的代碼塊非常優雅;我很喜歡它)=)我一定會把它保存在我的武庫中;然而,對於我目前的問題,它不是很有效=/ – 2010-06-09 04:27:08

+0

或使用'reject!',這相當於'delete_if'。 – Anurag 2010-06-09 04:37:44