Breadth-first version of itertools.chain()

后端 未结 2 1429
死守一世寂寞
死守一世寂寞 2021-01-23 23:58

In itertools there\'s chain, which combines multiple generators in a single one, and in essence does a depth-first iteration over them, i.e., cha

相关标签:
2条回答
  • 2021-01-24 00:28

    Not sure if you'd still consider this too "verbose"...

    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']
    
    0 讨论(0)
  • 2021-01-24 00:41

    You could use collections.deque() to rotate through your iterators; rotating a deque is much more efficient. I'd also call it a chained zip, not a 'breath first chain', as such:

    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)
    

    Demo:

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

    There is also a roundrobin() recipe in the documentation that does the same, using the 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))
    
    0 讨论(0)
提交回复
热议问题