【问题标题】:Can I manipulate the current iteration of the collection being currently iterated over?我可以操纵当前正在迭代的集合的当前迭代吗?
【发布时间】:2017-08-04 10:33:51
【问题描述】:

假设我有一个@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 指数上涨。

唯一不变的是,确定它如何通过原始迭代的方式是基于当前迭代块内发生的事情。

【问题讨论】:

  • @sschmeck, [1,2,3].method(:step) #Name error: undefined method 'step' for class 'Array'.
  • 不应该是if (index % 3 == 0)而不是if (index % 3 = 0)
  • @marcamillion 你总是跳过元素还是你也可以“跳回来”?
  • 我可能在这里遗漏了一些东西,但是next if condition_not_applicable? 有什么问题?
  • @fylooi:如果你想一次跳 几个 位置,你需要在每次迭代中跟踪它。 (跳过了吗?)。这需要更多代码。

标签: arrays ruby iterator enumerator


【解决方案1】:

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

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

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

【讨论】:

  • 正是因为我把它从我的脑海中去掉了,所以在你指出之前我从来没有想过它。
【解决方案2】:

可以使用临时累加器来做到这一点:

@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 设为一种方法,可以根据需要做出复杂的决策。

【讨论】:

    【解决方案3】:

    你让它变得比必要的复杂。你不需要在迭代中跳跃。当条件(不)满足时忽略该任务,然后进行下一次迭代。

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

    【讨论】:

    • 问题是可能需要3个nexts。
    • 正是@EricDuminil。这才是核心问题。不要只是假设迭代只是 1,它可能是 5 或 7。
    【解决方案4】:

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

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

    【讨论】:

    • e.any? 检查在存在StopIteration 处理时是相当多余的。可以很简单loop do.
    【解决方案5】:

    如果你想多次使用这个逻辑,你可能需要定义一个 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-28
      • 2014-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-29
      • 2018-06-01
      相关资源
      最近更新 更多