2016-02-19 3 views
5

sto iterazione di un array nidificato con due each blocchi, ed eliminare un elemento dalla stessa matrice all'interno della iterazione interno:elemento Delete matrice mentre iterazione

arr = [1,2,3] 
arr.each do |x| 
    arr.each do |y| 
    puts "#{arr.delete(y)}" 
    end 
end 

Questo produce i risultati 1, 3. L'array diventa [2].

Perché il valore 2 non è passato al primo o al secondo ciclo? Si tratta di una sorta di effetto collaterale dell'iterazione annidata?

+0

si prega di approvare risposta appropriata – bronislav

+0

@bronislav, dargli un po 'di tempo. È passata solo un'ora. Non c'è fretta. –

+0

@CarySwoveland, spiacente – bronislav

risposta

7

È a causa dell'indice dell'elemento eliminato. Ho aggiunto qualche uscita visualizzare:

arr = [1,2,3] 
arr.each do |x| 
    puts "1: #{arr.inspect}, x: #{x}" 
    arr.each do |y| 
    puts "2: #{arr.inspect}, y: #{y}" 
    puts "#{arr.delete(y)}" 
    end 
end 

Risultato:

1: [1, 2, 3], x: 1 
2: [1, 2, 3], y: 1 
1 
2: [2, 3], y: 3 
3 
=> [2] 

Il primo elemento eliminato è 1 (indice è 0) in ogni blocco interno. Dopo la cancellazione 2 ha l'indice 0 e ora ogni iterazione va all'indice 1 che ora è l'elemento 3. 3 verrà eliminato e questa è la fine dell'iterazione. Quindi ottieni [2].

Lo stesso avviene senza annidati ogni:

arr = [1,2,3] 
arr.each do |x| 
    puts "1: #{arr.inspect}, x: #{x}" 
    puts "#{arr.delete(x)}" 
end 

risultati:

1: [1, 2, 3], x: 1 
1 
1: [2, 3], x: 3 
3 
=> [2] 

Consiglio di usare reverse_each per tali operazioni per evitare questo comportamento:

arr = [1,2,3] 
arr.reverse_each do |x| 
    puts "1: #{arr.inspect}, x: #{x}" 
    puts "#{arr.delete(x)}" 
end 

risultati:

1: [1, 2, 3], x: 3 
3 
1: [1, 2], x: 2 
2 
1: [1], x: 1 
1 
=> [] 
+1

+1 del suggerimento costruttivo. O potresti semplicemente scegliere di usare 'select' o' reject' in queste situazioni – cozyconemotel

1

Per comprendere questo caso, prendiamo il caso che un semplice array venga attraversato con indice.

Si dispone di un array con [1,2,3].

Quando si avvia iterazione con 0, l'elemento corrente è 1. Ora, si cancella l'elemento 1 all'indice 0, l'array diventerà [2,3].

Nella prossima iterazione, l'indice sarà 1 e punterà a 3. E 3 verrà eliminato. Il tuo array sarà [2].

Ora, l'indice è 2 e l'array ha lunghezza 1. Quindi, non accadrà nulla. Ora, quando questo ciclo interno sarà completato, il ciclo esterno riprenderà dall'indice 1 aggiornato e poi da 2. E poiché la matrice ha lunghezza 1, non verrà eseguita.

Quindi, passando da questo, sembra che stia usando l'indice come iterazione.

Per quanto ne so, dovrebbe avere un comportamento non definito (come in C++ tale codice non è raccomandato). Perché, mentre si itera se si elimina l'elemento corrente, si corrompe il valore del puntatore (attualmente trattenuto nel parametro del blocco funzione passato a each).

+0

Il tuo primo paragrafo è la tua opinione/richiesta di funzione, e dovrebbe essere tenuto separato dal resto (forse essere messo alla fine all'interno di paraneesi). Penso che questo sia il motivo per cui non riesci a mandare in rialzo nonostante la risposta concisa.Non hai nemmeno menzionato il ciclo esterno (almeno devi spiegare come non ha effetto). – sawa

2

Non ha nulla a che fare con il nesting.In realtà, si ottiene lo stesso risultato con solo il ciclo interno:

arr = [1,2,3] 
arr.each do |y| 
    puts "#{arr.delete(y)}" 
end 
# => outputs 1, 3 
a # => [2] 

Il compication è dovuto alla modifica della matrice durante l'iterazione.


La ragione è che Array#each si basa sull'indice. Innanzitutto, x diventa 1 (che è completamente irrilevante per il risultato). All'interno del ciclo interno, prima devi:

  • a: [1, 2, 3], index: 0, y: 1

dove index è l'indice su cui si basa l'iterazione interna, e si elimina y e si ottiene:

  • a: [2, 3]
  • 012.

Nella prossima iterazione interna, si ha:

  • a: [2, 3], index: 1, y: 3

noti che 2 viene saltata a causa di iterazione si basa sull'indice (1). Poi, 3 viene eliminato, che dà:

  • a: [2].

Quando l'anello esterno tenta la prossima iterazione all'indice 1, non c'è abbastanza elementi lasciati in a, così finisce lì.

0

poiché il each itera con gli indici e si sta eliminando un elemento nel numero interno loop ogni altro elemento nella successiva iterazione. se aumenti il ​​numero di elementi e includi l'indice corrente dell'iterazione nel ciclo, potrai vedere l'immagine più grande.

arr = [1,2,3,4,5,6,7,8,9] 
arr.each_with_index do |x,ix| 
    puts "loop1: #{arr.inspect}, x: #{x}, ix: #{ix}" 
    arr.each_with_index do |y, iy| 
    puts "loop2: #{arr.inspect}, y: #{y}, iy: #{iy}" 
    puts "#{arr.delete(y)}" 
    end 
end 

risultato

loop1: [1, 2, 3, 4, 5, 6, 7, 8, 9], x: 1, ix: 0 
loop2: [1, 2, 3, 4, 5, 6, 7, 8, 9], y: 1, iy: 0 
1 
loop2: [2, 3, 4, 5, 6, 7, 8, 9], y: 3, iy: 1 
3 
loop2: [2, 4, 5, 6, 7, 8, 9], y: 5, iy: 2 
5 
loop2: [2, 4, 6, 7, 8, 9], y: 7, iy: 3 
7 
loop2: [2, 4, 6, 8, 9], y: 9, iy: 4 
9 
loop1: [2, 4, 6, 8], x: 4, ix: 1 
loop2: [2, 4, 6, 8], y: 2, iy: 0 
2 
loop2: [4, 6, 8], y: 6, iy: 1 
6 
=> [4, 8] 

poiché si elimina durante il ciclo e dopo ogni iterazione l'indice viene incrementato ma la matrice è un elemento breve, quindi, elimina il successivo (e tutti i) corrispondente elemento (s) disponibile e alla fine il ciclo confronta e ferma il ciclo quando index >= length