广度优先版本的itertools.chain()

时间:2014-05-26 16:20:29

标签: python itertools

itertools中有chain,它将多个生成器组合在一起,实质上是对它们进行深度优先迭代,即chain.from_iterable(['ABC', '123'])得到A,B,C ,1,2,3。但是,没有广度优先版本,或者我错过了什么?当然有izip_longest,但是对于大量的生成器来说,这感觉很尴尬,因为元组会非常长并且可能非常稀疏。

我想出了以下内容:

def chain_bfs(*generators):
    generators = list(generators)
    while generators:
        g = generators.pop(0)
        try:
            yield g.next()
        except StopIteration:
            pass
        else:
            generators.append(g)

对我来说感觉有点啰嗦,我错过了更多的Pythonic方法吗?这个函数是否适合包含在itertools

2 个答案:

答案 0 :(得分:7)

您可以使用collections.deque()来旋转迭代器;旋转双端队列效率更高。我也把它称为链式拉链,而不是第一链#',因此:

from collections import deque

def chained_zip(*iterables):
    iterables = deque(map(iter, iterables))
    while iterables:
        try:
            yield next(iterables[0])
        except StopIteration:
            iterables.popleft()
        else:
            iterables.rotate(-1)

演示:

>>> list(chained_zip('ABC', '123'))
['A', '1', 'B', '2', 'C', '3']
>>> list(chained_zip('AB', '1234'))
['A', '1', 'B', '2', '3', '4']

还有roundrobin() recipe in the documentation使用itertools.cycle() function

执行相同操作
def roundrobin(*iterables):
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
    # Recipe credited to George Sakkis
    pending = len(iterables)
    nexts = cycle(iter(it).__next__ for it in iterables)
    while pending:
        try:
            for next in nexts:
                yield next()
        except StopIteration:
            pending -= 1
            nexts = cycle(islice(nexts, pending))

答案 1 :(得分:1)

不确定你是否仍然认为这太“冗长”......

def chain_bfs2(*generators):
    generators = map(iter, generators)
    while generators:
        for i, generator in enumerate(generators):
            try:
                yield generator.next()
            except StopIteration:
                del generators[i]

print list(chain_bfs2('AB', '123'))  # ['A', '1', 'B', '2', '3']