Pergunta

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]

Esse mesmo efeito acontece com o outro iterador de matriz, 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(",")

tem a mesma saída.

A razão pela qual isso está acontecendo é bem claro ... Ruby não itera através dos objetos armazenados na matriz, mas apenas transforma -o em um iteador de índice de matriz bonito, começando no índice 0 e cada vez que aumenta o índice até que termine . Mas quando você exclui um item, ele ainda aumenta o índice, para que não avalie o mesmo índice duas vezes, o que eu quero.

este poderia Não seja o que está acontecendo, mas é o melhor que consigo pensar.

Existe uma maneira limpa de fazer isso? Já existe um iterador embutido que pode fazer isso? Ou terei que sujar e fazer um iterador de índice de matriz, e não incrementar quando o item é excluído? (ou itera através de um clone da matriz e exclua da matriz original)


Esclarecimento

Eu simplesmente não quero excluir itens de uma matriz; Desculpe se isso estava claro. O que eu gostaria de fazer é iterar em cada elemento e "processar"; Esse processo às vezes pode excluí -lo. Para ser mais preciso:

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

Idealmente, eu gostaria que todos os itens em rastreamento_array fossem processados.

Quando Living_thing é removido de rastrear_array, Living_thing#Die devo ser chamado; do_stuff_to_die limpa as coisas que precisam ser clanadas.

Foi útil?

Solução

Isso pode ser mais adequado para o processamento (ref. esclarecimento atualizado)

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

Side etapa a pergunta que você teve (sobre iteração através de objetos versus iteração através de índices)

Outras dicas

É um erro na maioria dos idiomas para mudar uma coleção enquanto você itera. Na maioria dos idiomas, a solução é criar uma cópia e mudar isso ou criar uma lista de índices e executar as operações nesses índices quando você está fazendo iteração, mas os iteradores de Ruby oferecem um pouco mais do que isso. Algumas soluções são óbvias. A IMO mais idiomática:

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

Mais diretamente traduzido do seu exemplo:

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

(delete_if é basicamente a versão destrutiva de reject, que retorna uma matriz dos objetos para os quais o bloco não retornou verdadeiro.)

Ok, então digamos que você queira eliminar todos os 2 da sua matriz:

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"

Mas eu suspeito que você queira iterar, então eu faria:

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

Talvez isso esteja muito sujo? A tarefa a nil mantém a contagem de iteradores corretamente, e o compact! Limpe os nilos após o fato. Curso map Mantém um pouco mais curto e é mais limpo:

arr.map {|x| x if x != 2}.compact!
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top