Frage

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]

Der gleiche Effekt tritt mit dem anderen Array Iterator, 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(",")

hat die gleiche Ausgabe.

Der Grund, warum dies geschieht ist ziemlich klar ... Rubin nicht wirklich Iterierte durch die Objekte in dem Array gespeichert, sondern dreht sich nur um es in einem hübschen Array-Index Iterator, beginnend bei Index 0 und jedes Mal Erhöhung der Index bis es vorbei ist. Aber wenn Sie ein Element löschen, erhöht er noch den Index, so dass es nicht zu bewerten den gleichen Index zweimal, was ich will es.

Das Macht nicht, was passiert ist, aber es ist das Beste, was ich denken kann.

Gibt es eine saubere Art und Weise, dies zu tun? Gibt es bereits einen eingebauten in Iterator, kann dies tun? Oder muss ich es zu schmutzig und ein Array-Index Iterator tun, und nicht erhöht werden, wenn das Element gelöscht wird? (Oder eine Iteration durch einen Klon des Arrays, und Löschen des ursprünglichen Arrays)


Klarstellung

Ich will nicht einfach Elemente aus einem Array löschen; sorry, wenn das war klar. Was ich will, würde zu tun ist, durchläuft jedes Element, und „Prozess“ es; Dieser Vorgang kann manchmal löschen. Um genauer zu sein:

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

Idealerweise würde ich alle Elemente in tracking_array will verarbeitet werden.

Wenn Living_Thing von tracking_array entfernt wird, # sterben Living_Thing muss genannt werden; do_stuff_to_die reinigt Dinge, die claned bis zu haben.

War es hilfreich?

Lösung

Dies könnte mehr für die Verarbeitung geeignet sein (Ref. Aktualisiert Klärung)

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

es Seitenrammschutz die Frage, die Sie allerdings hatte (etwa Iteration durch Objekte vs Iteration durch Indizes)

Andere Tipps

Es ist ein Fehler in den meisten Sprachen eine Sammlung mutieren während Sie es laufen. In den meisten Sprachen ist die Lösung eine Kopie und mutieren, dass oder Aufbau einer Liste von Indizes zu erstellen und die Operationen auf diesen Indizes durchführen, wenn Sie fertig Iterieren sind, aber Rubys Iteratoren geben Sie ein bisschen mehr als das. Ein paar Lösungen liegen auf der Hand. Die meisten idiomatische IMO:

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

Weitere direkt von Ihrem Beispiel übersetzt:

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

(delete_if ist im Grunde die zerstörerische Version von reject, die eine Anordnung der Objekte zurückgibt, dass der Block nicht zurückkehrte gilt für.)

Okay, also lassen Sie uns sagen, Sie alle 2 des von Ihrem Array beseitigen wollen:

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"

Aber ich vermute, Sie Iterierte wollen, so würde ich:

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

Vielleicht ist das zu schmutzig? Die Zuordnung zu null hält den Iterator zählt richtig, und die compact! tilgt den nils nach der Tat. Course map hält es ein wenig kürzer und sauberer:

arr.map {|x| x if x != 2}.compact!
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top