让我们一步一步来:
arr = [10, 20, 30, 40]
enum0 = arr.each
#=> #<Enumerator: [10, 20, 30, 40]:each>
enum1 = enum0.with_index
#=> #<Enumerator: #<Enumerator: [10, 20, 30, 40]:each>:with_index>
我们可以通过将enum1转换为数组来查看其内容:
enum1.to_a
#=> [[10, 0], [20, 1], [30, 2], [40, 3]]
这告诉我们Enumerator#each(将调用Array#each)会将枚举器enum1 的四个元素传递到块中,依次将它们分配给块变量。第一个是:
elmt, i = enum1.next
#=> [10, 0]
puts elmt, i
# 10
# 0
elmt == 20
#=> false
所以arr.delete_at(i) 没有被执行。
arr 和 enum1 均未更改:
arr
#=> [10, 20, 30, 40]
enum1.to_a
#=> [[10, 0], [20, 1], [30, 2], [40, 3]]
each 现在将enum1 的下一个元素传递到块中:
elmt, i = enum1.next
#=> [20, 1]
elmt == 20
#=> true
所以我们执行:
arr.delete_at(i)
#=> [10, 20, 30, 40].delete_at(1)
#=> 20
arr
#=> [10, 30, 40]
enum1.to_a
#=> [[10, 0], [30, 1], [40, 2]]
啊!所以枚举器和arr 都已更改。这是完全有道理的,因为当创建枚举器时,会建立对原始接收器的引用,以及要使用它做什么的规则。因此,对接收器的更改将影响枚举器。
我们可以使用Enumerator#peek 来查看enum1 中each 将传递到块中的下一个元素:
enum1.peek
#=> [40, 2]
所以你看到each 移动到下一个索引位置,忽略了更早的元素已被删除的事实,导致后面的元素每个向下移动一个位置,导致each 跳过@987654343 @。
elmt, i = enum1.next
#=> [40, 2]
elmt == 20
#=> false
arr
#=> [10, 30, 40]
enum1.to_a
#=> [[10, 0], [30, 1], [40, 2]]
此时each 到达枚举器的末尾,所以它的工作完成了。因此它返回原始接收者arr,但它已被修改,所以我们得到:
[10, 30, 40]
一个更好的例子可能是:
arr = [10, 20, 20, 40]
地点:
[10, 20, 40]
将被退回。