14

我注意到它itertools(在我看来)没有能够将来自其他几个可迭代对象的元素交错的功能(而不是压缩它们):

def leaf(*args): return (it.next() for it in cycle(imap(chain,args)))
tuple(leaf(['Johann', 'Sebastian', 'Bach'], repeat(' '))) => ('Johann', ' ', 'Sebastian', ' ', 'Bach', ' ')

(编辑)我问的原因是因为我想避免不必要的 zip/flatten 事件。

显然,定义leaf很简单,但是如果有一个预定义的函数可以做同样的事情,我更愿意使用它,或者一个非常清晰的生成器表达式。是否有这样的内置函数,在 itertools 或其他一些著名的库中,或合适的惯用表达式?

编辑 2:一个更简洁的定义是可能的(使用functional包):

from itertools import *
from functional import *

compose_mult = partial(reduce, compose)
leaf = compose_mult((partial(imap, next), cycle, partial(imap, chain), lambda *args: args))
4

3 回答 3

15

您正在寻找内置zipitertools.chain.from_iterable展平结果:

>>> import itertools
>>> list(zip(['Johann', 'Sebastian', 'Bach'], itertools.repeat(' ')))
[('Johann', ' '), ('Sebastian', ' '), ('Bach', ' ')]
>>> list(itertools.chain.from_iterable(_))
['Johann', ' ', 'Sebastian', ' ', 'Bach', ' ']

请注意,我过去list只是为了强制输出一个不错的结果。使用标准的 itertools,替代实现leaf将是:

leaf = lambda *a: itertools.chain.from_iterable(itertools.izip(*a)) # Python 2.x
leaf = lambda *a: itertools.chain.from_iterable(zip(*a))            # Python 3.x
于 2012-01-07T12:57:48.003 回答
8

itertools roundrobin() 配方将是我的第一选择,尽管在您的确切示例中它将产生一个无限序列,因为它以最长的可迭代停止,而不是最短的。当然,这很容易解决。也许值得一试不同的方法?

于 2012-01-07T15:36:04.960 回答
0

这个自定义函数交错迭代器并继续执行,直到每个迭代器都被耗尽

def interleave_iterators(*iterators):
    finished = [False for x in range(len(iterators))]
    stop_cond = functools.reduce(lambda x,y:not x or not y,finished)
    while stop_cond:
        for i,it in enumerate(iterators):
            try:
                yield next(it)
            except StopIteration:
                finished[i] = True
        stop_cond = functools.reduce(lambda x,y:not x or not y,finished)

例如

it1 = iter([2,4,6,8])
it2 = iter([1,3,5,7,9,11,13])
for x in interleave_iterators(it1,it2): # prints 2 1 4 3 6 5 8 7 9 11 13
    print(str(x),end=" ")
于 2021-08-03T08:47:28.317 回答