【问题标题】:Breadth-first version of itertools.chain()itertools.chain() 的广度优先版本
【发布时间】:2014-07-15 11:16:11
【问题描述】:

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 中?

【问题讨论】:

  • 所以你基本上想在这里链接zip()的输出?我不会将其称为呼吸优先与深度优先(这意味着更深的树)。
  • 对于给定的示例,我希望生成的生成器输出 A、1、B、2、C、3。并且,如果提供了“AB”和“1234”,则为 A、1、B、2、3、4。

标签: python itertools


【解决方案1】:

您可以使用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))

【讨论】:

  • 呵呵,我个人比roundrobin from the recipies section of the docs 更清楚。不错不错。
  • @Veedrac:啊,我知道这也有一个 itertools 配方,谢谢你提醒我它的名字。 :-)
  • 啊,我错过了食谱部分。这似乎相当优雅地解决了它:) 不确定我更喜欢哪个版本,第一个版本更具可读性,虽然 next 的巧妙使用也吸引了我。
【解决方案2】:

不确定您是否还会认为这太“冗长”...

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']

【讨论】:

    猜你喜欢
    • 2010-10-15
    • 1970-01-01
    • 2018-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多