Python:在同一个列表中进行双重/多重迭代的优雅方式

时间:2009-05-15 10:35:15

标签: python iterator

我已经编写了一些代码,如下所示,将项目与列表中的其他项目进行比较。这种双重迭代是否有更优雅的模式?

jump_item_iter = (j for j in items if some_cond)
try:
    jump_item = jump_item_iter.next()
except StopIteration:
    return
for item in items:
    if jump_item is item:
        try:
            jump_item = jump_iter.next()
        except StopIteration:
            return
    # do lots of stuff with item and jump_item

我不认为“except StopIteration”非常优雅

编辑:

为了更清楚,我希望访问列表中的每个项目,并将其与列表中的下一个项目(jump_item)配对,以满足some_cond。

17 个答案:

答案 0 :(得分:4)

据我所知,任何现有的解决方案都适用于一般的一次性,可能无限的 ator ,所有这些解决方案似乎都需要一个强大的能力

这是解决方案。

def batch_by(condition, seq):
    it = iter(seq)
    batch = [it.next()]
    for jump_item in it:
        if condition(jump_item):
            for item in batch:
                yield item, jump_item
            batch = []
        batch.append(jump_item)

这很容易在无限迭代器上工作:

from itertools import count, islice
is_prime = lambda n: n == 2 or all(n % div for div in xrange(2,n))
print list(islice(batch_by(is_prime, count()), 100))

这将打印前100个整数及其后面的素数。

答案 1 :(得分:1)

我不知道compare()正在做什么,但80%的情况下,你可以用一个简单的字典或一对字典来做到这一点。在列表中跳转是一种线性搜索。线性搜索 - 在可能的范围内 - 应始终用直接引用(即dict)或树搜索(使用bisect模块)替换。

答案 2 :(得分:1)

编写生成器函数:

def myIterator(someValue):
    yield (someValue[0], someValue[1])

for element1, element2 in myIterator(array):
     # do something with those elements.

答案 3 :(得分:1)

这个怎么样?

paired_values = []
for elmt in reversed(items):
    if <condition>:
        current_val = elmt
    try:
        paired_values.append(current_val)
    except NameError:  # for the last elements of items that don't pass the condition
        pass
paired_values.reverse()

for (item, jump_item) in zip(items, paired_values):  # zip() truncates to len(paired_values)
    # do lots of stuff

如果项目的第一个元素匹配,则将其用作jump_item。这是与原始代码的唯一区别(您可能希望这种行为)。

答案 4 :(得分:1)

以下迭代器是时间和内存效率:

def jump_items(items):
    number_to_be_returned = 0
    for elmt in items:
        if <condition(elmt)>:
            for i in range(number_to_be_returned):
                yield elmt
            number_to_be_returned = 1
        else:
            number_to_be_returned += 1

for (item, jump_item) in zip(items, jump_items(items)):
    # do lots of stuff

请注意,您实际上可能希望将第一个number_to_be_returned设置为1 ...

答案 5 :(得分:0)

您可以执行以下操作:

import itertools

def matcher(iterable, compare):
    iterator= iter(iterable)
    while True:
        try: item= iterator.next()
        except StopIteration: break
        iterator, iterator2= itertools.tee(iterator)
        for item2 in iterator2:
            if compare(item, item2):
                yield item, item2

但是它非常复杂(实际上效率不高),如果你只是做了一个就更简单了

items= list(iterable)

然后只在items上写两个循环。

显然,这不适用于无限可迭代,但您的规范只适用于有限迭代。

答案 6 :(得分:0)

我不知道你要用这​​个代码做什么。但我99%肯定无论它是什么,都可以用2行完成。我也觉得'=='运算符应该是'is'运算符,否则compare()函数在做什么?如果从第二个jump_iter.next调用返回的项目也等于'item'会发生什么?似乎算法会做错误的事情,因为你会比较第二个而不是第一个算法。

答案 7 :(得分:0)

您可以将循环体编写为:

import itertools, functools, operator

for item in items:
    jump_item_iter = itertools.dropwhile(functools.partial(operator.is_, item), 
                                         jump_item_iter)

    # do something with item and jump_item_iter

dropwhile会返回一个迭代器,它会跳过符合条件的所有符号(此处为“is item”)。

答案 8 :(得分:0)

也许为时已晚,但是:

l = [j for j in items if some_cond]
for item, jump_item in zip(l, l[1:]):
    # do lots of stuff with item and jump_item

如果l = [j,范围内的j为j(10),如果j%2 == 0]则迭代结束:[(0,2),(2,4),(4,6),(6 ,8)]。

答案 9 :(得分:0)

使用itertools.groupby更好:

def h(lst, cond):
  remain = lst
  for last in (l for l in lst if cond(l)):
    group = itertools.groupby(remain, key=lambda x: x < last)
    for start in group.next()[1]:
      yield start, last
    remain = list(group.next()[1])

使用方法: lst =范围(10) cond = lambda x:x%2 打印清单(h(lst,cond))

将打印

[(0, 1), (1, 3), (2, 3), (3, 5), (4, 5), (5, 7), (6, 7), (7, 9), (8, 9)]

答案 10 :(得分:0)

只使用迭代器

def(lst, some_cond):
      jump_item_iter = (j for j in lst if som_cond(j))
      pairs = itertools.izip(lst, lst[1:])
      for last in jump_item_iter:
        for start, start_next in itertools.takewhile(lambda pair: pair[0] < last, pairs):
          yield start, last
        pairs = itertools.chain([(start_next, 'dummy')], pairs)

输入:range(10)和some_cond = lambda x:x%2 给出[(0,1),(1,3),(2,3),(3,5),(4,5),(5,7),(6,7),(7,9), (8,9)] (与你的例子相同)

答案 11 :(得分:0)

for i in range( 0, len( items ) ):
    for j in range( i+1, len( items ) ):
        if some_cond:
            #do something
            #items[i] = item, items[j] = jump_item

答案 12 :(得分:0)

我的第一个答案是错误的,因为我不太明白你想要达到的目的。所以,如果我理解正确(这次,我希望),你希望主for item in items:在追踪过滤掉一些项的迭代器之后“追逐”。好吧,你可以做的并不多,除非将它包装成chase_iterator(iterable, some_cond)生成器,这会使你的主代码更具可读性。

也许更可读的方法是“累加器方法”(如果compare()的顺序无关紧要),例如:

others = []
for item in items:
    if some_cond(item):
        for other in others:
            compare(item, other)
        others = []
    else:
        others.append(item)

(伙计,我开始讨厌Stack Overflow ......太上瘾......)

答案 13 :(得分:0)

这是一个看起来更清洁的简单解决方案:

for i, item in enumerate(items):
    for next_item in items[i+1:]:
        if some_cond(next_item):
            break
    # do some stuff with both items

缺点是您多次检查next_item的条件。但你可以很容易地优化它:

cond_items = [item if some_cond(item) else None for item in items]
for i, item in enumerate(items):
    for next_item in cond_items[i+1:]:
        if next_item is not None:
            break
    # do some stuff with both items

然而,这两个解决方案都比问题的原始解决方案带来更多的开销。当你开始使用计数器来解决这个问题时,我认为最好直接使用迭代器接口(就像在原始解决方案中一样)。

答案 14 :(得分:0)

您是否基本上尝试将迭代器中的每个项目与原始列表中的其他项目进行比较?

在我看来,这只是一个使用两个循环的情况,而不是试图将它合二为一。


filtered_items = (j for j in items if some_cond)
for filtered in filtered_items:
    for item in items:
        if filtered != item:
            compare(filtered, item)

答案 15 :(得分:0)

因此,您希望比较同一列表中的项目对,该对中的第二项必须满足某些条件。通常,当您想要比较列表中的对时,请使用zip(或itertools.izip):

for item1, item2 in zip(items, items[1:]):
    compare(item1, item2)

弄清楚如何使some_cond适合这个:)

答案 16 :(得分:0)

你可以把整个迭代放到一个try结构中,这样会更清楚:

jump_item_iter = (j for j in items if some_cond)
try:
    jump_item = jump_item_iter.next()
    for item in items:
        if jump_item is item:
            jump_item = jump_iter.next()

    # do lots of stuff with item and jump_item

 except StopIteration:
     pass