简体   繁体   中英

How does modifying an array while iterating work in Ruby?

Given an array in Ruby:

numbers = [1, 2, 3, 4]

I'm confused about the result of what happens when modifying the array while iterating over the array:

numbers.each do |number|
  p number
  numbers.shift(1)
end

The output I get when running the code is 1 and 3. The 1 makes perfect sense for the first loop, but I can't seem to figure out why the program is returning a 3.

I modified the original code so I could see what is occurring at each step along the way, and included the hash marks "----" to know when each loop is finished running:

numbers = [1, 2, 3, 4]
numbers.each_with_index do |number, index|
  p "Array is #{numbers} and index is #{index}"
  p "Number is #{number}"
  numbers.shift(1)
  p "The Array after shift is now #{numbers}, number is #{number}, and the index is now #{index}"
  p "----"
end

Through the first loop, shift successfully removes the 1 from the original array and the array becomes [2,3,4], while "number" is 1. But on the second loop through, instead of "number" returning 2 it returns 3.

The only way I think it makes sense is if the index itself must be determining which item to remove from the array. When the index is 0 it's removing the array item at index 0 from the array, and when the index is 1 it's removing array item at index 1 from the updated array. Then the loop stops when the index is 2 because the array no longer has an item at index 2.

This is a question as part of the Launch School course. I can't figure out why the code returns what it does. I also tried inserting binding.pry both before and after the numbers.shift(1) is called, but it didn't seem to clarify anything to me.

numbers = [1, 2, 3, 4]

Okay, here we go with

numbers.each do |number|
  # do stuff
end

First number

p number # 1, we are on the first element of [1, 2, 3, 4]
numbers.shift(1) # numbers is now [2, 3, 4]

Second number

p number # 3, we are on the second element of [2, 3, 4]
numbers.shift(1) # numbers is now [3, 4]

Third number – wait, there is no third element of [3, 4] , we're done.

do not look at it as values but rather as an address.
Enumerator(or the Iteration) follows the next address pointed by the current one.

1st loop:
  1. number points to the first address (with value 1)
  2. shifting array making the current 2nd address the 1st, therefore the value 2 is now on the 1st address.

2nd loop:
  1. number points to the 2nd address (with value 3)
  2. shifting array making the current 2nd address the 1st

3rd loop:
  ※same logic until 'StopIteration' is met

In regards to 'shift', it will just remove the 1st item without overthinking.

reference: https://apidock.com/ruby/Enumerator/next_values

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM