このルビーイテレータtrickinessにクリーンソリューション?
-
25-09-2019 - |
質問
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
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ががが呼び出されなければなりません#ダイをLiving_Thing、tracking_arrayから削除されます。; do_stuff_to_dieアップclanedする必要が物事をクリーンアップします。
解決
これはより多くの処理に適しされる可能性があります(参考文献更新明確化)
k = [1,2,3,4,5]
k.dup.each do |n|
puts n
if n == 2
k.delete(n)
end
end
puts k.join(",")
その辺は(インデックスによる反復対オブジェクトを通じて反復程度)
けれども、あなたが持っていた疑問ステップ他のヒント
それはあなたがそれを反復している間、コレクションを変異させるほとんどの言語でのエラーです。ほとんどの言語では、溶液は、というコピーとのmutateを作成したり、インデックスのリストを構築し、あなたが完了反復処理をしているとき、それらのインデックスに対して操作を実行することですが、Rubyのイテレータは、もう少しそれよりもあなたを与えます。ソリューションのカップルは明白です。最も慣用IMOます:
puts k
puts k.reject {|n| n == 2}.join(',')
より直接あなたの例から翻訳ます:
k.delete_if do |n|
puts n
n == 2
end
puts k.join(',')
(delete_if
がブロックのためにtrueを返していないことオブジェクトの配列を返す、基本的にreject
の破壊的なバージョンである。)
わかりましたので、あなたはあなたの配列からすべての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!