我可以操纵当前正在迭代的集合的当前迭代吗?

Can I manipulate the current iteration of the collection being currently iterated over?

假设我有一个 @lines 的集合,我想对其进行迭代,但我想根据集合的内容来操纵它的迭代方式,我该怎么做?

即像这样:

@lines.each_with_index do |line, index|
   if (index % 3 = 0)
      jump_to index == next_multiple_of_3
   end
end

所以看起来可能是这样的:

Element | Index
a       | 1
b       | 2
c       | 3
f       | 6
g       | 7
h       | 8
i       | 9
l       | 12

我该怎么做?

编辑 1

请注意,正如我在下面的评论中所解释的,我不一定总是想跳转到特定的倍数。基本上,我想跳转到某个由当前迭代中发生的事情决定的任意索引。

所以第一次绕圈时,它可以向上跳跃 3 个索引,但是下一次它向上跳跃 7 个索引,然后接下来的 5 次它向上跳跃 1 像往常一样,然后它向上跳 2 个索引。

唯一不变的是,确定它在原始迭代中的进展情况的方式是基于当前迭代块内发生的事情。

使用 while 循环并自行管理索引。多年来,我们一直在劝阻人们不要这样做。但这是一种合适的情况。 :)

idx = 0
while idx < @lines.length
  line = @lines[idx]
  idx += condition ? x : y # or whatever logic you have
end

当然,这假设@lines能够随机访问。如果不是,则将其设为数组。

可以使用临时累加器来完成:

@lines = (1..12).map(&:to_s)
JUMP_BY = 3
@lines.each.with_index.reduce(0) do |acc, (line, index)|
  next acc unless index == acc

  puts "line: #{line}"
  q, mod = index.divmod(JUMP_BY)
  (mod == JUMP_BY - 1) && (q % 2).zero? ? \
    (q + 2) * JUMP_BY - 1 : index + 1 # next 
end
#⇒ line: 1
#  line: 2
#  line: 3
#  line: 6
#  line: 7
#  line: 8
#  line: 9
#  line: 12

通过JUMP_BY一种方法,人们可能会根据需要做出复杂的决定。

你把它变得比必要的更复杂了。你不需要跳进迭代。条件不满足时忽略任务,进入下一次迭代

@lines.each_with_index do |line, index|
  next if index % 3 == 0
  ... # do the contentful things
end

通过在枚举器上使用 next,您可以跳过 x 步

def walk
    e = ('a'..'z').each
    while e.any?
        rand(5).times{e.next}
        puts e.peek
    end
rescue StopIteration => e
end

如果您想多次使用此逻辑,您可能需要定义一个 Enumerable 方法:

module Enumerable
  def skip
    Enumerator.new do |yielder|
      skip_count = 0
      each_with_index do |object, index|
        if skip_count > 0
          skip_count -= 1
        else
          yielder << object
          skip_count = yield(object, index) || 0
        end
      end
    end
  end
end

您可以在任何 Enumerable 上使用它,并且您可以指定应跳过的元素数,具体取决于当前元素和索引。

对于您的示例,您希望每 6 个元素跳过 2 个元素(例如 4 和 5)。但是有一个偏移,你想跳过 index = 2 之后的元素(Ruby 索引是基于 0 的):

puts ('a'..'z').skip{ |_, index| 2 if (index - 2) % 6 == 0 }.take(8)
# a
# b
# c
# f
# g
# h
# i
# l