【问题标题】:python arbitrarily incrementing an iterator inside a looppython在循环内任意递增迭代器
【发布时间】:2010-12-01 06:31:42
【问题描述】:

我可能以错误的方式处理这个问题,但我想知道如何在 python 中处理这个问题。

首先是一些c代码:

int i;

for(i=0;i<100;i++){
  if(i == 50)
    i = i + 10;
  printf("%i\n", i);
}

好吧,所以我们永远不会看到 50 年代...

我的问题是,我怎样才能在 python 中做类似的事情?例如:

for line in cdata.split('\n'):
  if exp.match(line):
    #increment the position of the iterator by 5?
    pass
  print line

由于我在 python 方面的经验有限,我只有一个解决方案,引入一个计数器和另一个 if 语句。在 exp.match(line) 为真后中断循环直到计数器达到 5。

必须有更好的方法来做到这一点,希望不涉及导入另一个模块。

提前致谢!

【问题讨论】:

  • 请删除令人困惑和不相关的 C 代码,并专注于您的实际 Python 问题。
  • @S.Lott 我很高兴他没有服从你

标签: python iterator


【解决方案1】:

Python 中有一个很棒的包,叫做itertools

但在我开始之前,先解释一下迭代协议是如何在 Python 中实现的。当您想要在容器上提供迭代时,您指定提供iterator type__iter__() 类方法。 "Understanding Python's 'for' statement" 是一篇很好的文章,介绍了 for-in 语句在 Python 中的实际工作原理,并很好地概述了迭代器类型的工作原理。

看看以下内容:

>>> sequence = [1, 2, 3, 4, 5]
>>> iterator = sequence.__iter__()
>>> iterator.next()
1
>>> iterator.next()
2
>>> for number in iterator:
    print number 
3
4
5

现在回到itertools。该包包含用于各种迭代目的的函数。如果您需要进行特殊排序,这是首先要研究的地方。

您可以在底部找到 Recipes 部分,其中包含使用现有 itertools 作为构建块创建扩展工具集的秘诀

还有一个有趣的功能可以满足您的需求:

def consume(iterator, n):
    '''Advance the iterator n-steps ahead. If n is none, consume entirely.'''
    collections.deque(itertools.islice(iterator, n), maxlen=0)

这是一个关于其工​​作原理的快速、易读的示例(Python 2.5)

>>> import itertools, collections
>>> def consume(iterator, n):
    collections.deque(itertools.islice(iterator, n))
>>> iterator = range(1, 16).__iter__()
>>> for number in iterator:
    if (number == 5):
        # Disregard 6, 7, 8, 9 (5 doesn't get printed just as well)
        consume(iterator, 4)
    else:
        print number

1
2
3
4
10
11
12
13
14
15

【讨论】:

  • 请注意,这是迭代的,与原始代码不同。如果您使用很大的范围,例如,您可以看到差异。 xrange(1000000000),并进行非常大的跳跃。通常这无关紧要,但在某些算法中它可能(例如,如果生成项目很昂贵),所以要注意它。只是没有 API 可以要求迭代器跳过项目;它必须迭代地运行它。
  • 您可能希望使用iterator = iter(range(1,16)) 而不是__iter__ 的东西。 (题外话:xrange 可以更好地在此处提供此特定代码,但对于 learning 迭代器来说是一个不令人满意的示例。)
  • 引用的配方实际上建议使用next(islice(iterator, n, n), None) 而不是使用maxlen=0 出队。有区别吗?
【解决方案2】:

itertools.islice:

lines = iter(cdata.splitlines())
for line in lines:
    if exp.match(line):
       #increment the position of the iterator by 5
       for _ in itertools.islice(lines, 4):
           pass
       continue # skip 1+4 lines
    print line

例如,如果expcdata 是:

exp = re.compile(r"skip5")
cdata = """
before skip
skip5
1 never see it
2 ditto
3 ..
4 ..
5 after skip
6 
"""

那么输出是:

跳过之前 5 跳过后 6

C 示例的 Python 实现

i = 0
while i < 100:
    if i == 50:
       i += 10
    print i
    i += 1

正如@[Glenn Maynard] 在the comment 中指出的那样,如果您需要进行非常大的跳转,例如 i += 100000000,那么您应该使用显式的while 循环,而不是仅仅跳过for 循环中的步骤.

这是使用显式 while 循环而不是 islice 的示例:

lines = cdata.splitlines()
i = 0
while i < len(lines):
    if exp.match(lines[i]):
       #increment the position of the iterator by 5
       i += 5
    else:
       print lines[i]
       i += 1

此示例产生与上述islice 示例相同的输出。

【讨论】:

    【解决方案3】:

    如果您使用数字进行操作,则列表推导可以工作:

    for i in [x for x in range(0, 99) if x < 50 and x > 59]:
        print i
    

    不过,向前移动迭代器有点困难。如果您不想使用计数器方法,我建议您事先设置您的列表,可能通过拆分 cdata,然后计算出匹配行的索引并删除该行和以下行。除此之外,您还坚持使用反方法,老实说,这并不像您所说的那么令人不快。

    另一个选项是这样的:

    iterator = iter(cdata.split('\n'))
    for line in iterator:
        if exp.match(line):
            for i in range(0, 5):
                try:
                    iterator.next()
                except StopIteration:
                    break
        else:
            print line
    

    【讨论】:

    • “如果 x 59”不应该是“如果 x 59”吗?
    • 要使用迭代器,你能不能用生成器表达式替换列表推导式?也就是说,不是 [x for x in ...],而是做 (x for x in ...)。这将阻止创建另一个列表。
    【解决方案4】:

    不完全确定我是否遵循您的思考过程,但这里有一些东西可以借鉴..

    for i in range(len(cdata.split('\n'))):
      if i in range(50,60): continue
      line = cdata[i]
      if exp.match(line):
        #increment the position of the iterator by 5?
        pass
      print line
    

    不确定您真正追求的是什么,但 range(len(..)) 应该可以帮助您。

    【讨论】:

    • 是的 - 枚举绝对是我应该做的。取点。我投票给了我上面的那个。好多了——还有很多东西要学。
    【解决方案5】:

    您可以从迭代器中删除值

    def dropvalues(iterator, vals):
        for i in xrange(vals): iterator.next()
    

    现在只需确保您有一个迭代器对象可以使用lines = iter(cdata.split('\n'));并循环它。

    【讨论】:

      【解决方案6】:

      也许使用基因表达式。不漂亮但是...

      类似的东西:

      >>> gx = (line for line in '1 2 x 3 4 5 6 7 x 9 10 11 12 x 1'.split('\n'))
      >>> for line in gx:
      ...   if line == 'x':
      ...      for i in range(2):
      ...          line = gx.next()
      ...   print line
      

      唯一的问题是确保 gx 可以被 next()-ed。上例故意由于最后一个 x 产生异常。

      【讨论】:

        【解决方案7】:

        对于您的示例,当您使用列表(可索引序列)而不是迭代器时,我会推荐以下内容:

        lines = cdata.split("\n")
        for line in lines[:50]+lines[60:]:
          print line
        

        这不是最有效的,因为它可能会构造 3 个新列表(但如果跳过的部分比处理的部分大,它可能比其他选项更有效),但它非常干净和明确。

        如果您不介意使用 itertools 模块,您可以轻松地将列表转换为序列:

        from itertools import chain, islice
        for line in chain(islice(lines, None, 50), islice(lines, 60,None)):
          print line
        

        【讨论】:

          【解决方案8】:

          我无法很好地解析问题,因为存在这块令人困惑且不相关的 C 代码。请删除它。

          只关注 Python 代码和如何跳过 5 行的问题...

          lineIter= iter( cdata.splitlines() )
          for line in lineIter:
            if exp.match(line):
              for count in range(5):
                  line = lineIter.next()
            print line
          

          【讨论】:

          • 它是简单易读的 C 代码。我一眼就明白了他想做什么。代码几乎总是最清晰的解释。如果你看不懂 C,那是你的问题。
          • 我读C很好。我无法将 C 示例与问题陈述相协调。我发现 C 代码完全可读,但与提供的问题陈述无关。我可以——并且确实——理解了 C 代码。我没有看到C和问题之间的关系。
          • 我只是在寻找一种在 python 中任意最小化循环迭代的方法。 c代码与查询完全相关,实际上是我提交查询的原因。如果我不知道如何用另一种语言做这样的事情,我会导入 itertools 并完成它。对于我的原始查询,似乎没有办法在 python 中使用与 c 相同数量的迭代来完成相同或相似的任务。这就是为什么 perl 同时具有 foreach 和 for 语句的原因,我敢打赌(不是真的)。感谢大家的回答和反馈
          • 您可以不断重复代码是完全相关的。但是,我仍然看不到任何相关性。解决这个问题以显示清晰但不相关的 C 代码块和 Python 之间的某种逻辑联系对于那些绝对看不到任何关系的愚蠢的人(比如我)很有帮助。
          猜你喜欢
          • 2016-07-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-11-23
          • 2014-05-24
          • 2016-01-02
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多