简体   繁体   English

产生两个列表的交替值的生成器 function

[英]Generator function that yields alternating values of two lists

The question seems very basic and easy at first glance, but I still had some trouble writing an efficient solution.这个问题乍一看似乎非常基础和简单,但我在编写有效的解决方案时仍然遇到了一些麻烦。 The idea is to write a function, which takes two generators as an input and yields one item of each list.想法是编写一个 function,它以两个生成器作为输入并生成每个列表的一项。 If either list is empty, it yields each item of the remaining list.如果任一列表为空,则生成剩余列表的每个项目。 Example:例子:

 list(alternate("abcdefg", [1, 2, 3, 4])) == ["a", 1, "b",2, "c", 3, "d", 4, "e", "f", "g"]

what kind of worked, but looks very ugly:什么样的工作,但看起来很丑陋:

def alternate(xs1, xs2):
    xs1 = iter(xs1)
    xs2 = iter(xs2)
    while xs1 and xs2:
        try:
            yield next(xs1), next(xs2)
        except StopIteration:
                for x1 in xs1:
                    yield x1
                for x2 in xs2:
                    yield x2

Especially I want to avoid transforming the inputs to an iterator in the beginning.特别是我想避免在开始时将输入转换为迭代器。 I tried using 2 simple for loops, but this obviously returned only the last value of each list, because one for loops finishes before the other starts.我尝试使用 2 个简单的 for 循环,但这显然只返回每个列表的最后一个值,因为一个 for 循环在另一个循环开始之前完成。 This is not a solve my problem question, I am rather looking for some code to improve writing code skills!这不是解决我的问题的问题,我是在寻找一些代码来提高编写代码的技巧!

Use zip_longest and chain.from_iterable together:一起使用zip_longestchain.from_iterable

>>> from itertools import chain, zip_longest
>>> [ x for x in chain.from_iterable(zip_longest("abcdefg", [1,2,3,4])) if x is not None]
['a', 1, 'b', 2, 'c', 3, 'd', 4, 'e', 'f', 'g']

For something more explicit and similar to what you already have, handle each call to next separately, and break out of the loop immediately to iterate over the remainder of each iterator.对于更明确且与您已经拥有的内容相似的内容,请分别处理对next的每个调用,并立即跳出循环以迭代每个迭代器的其余部分。 Use itertools.cycle to alternate between the two iterators until one is exhausted.使用itertools.cycle在两个迭代器之间交替,直到一个迭代器用完。

from itertools import cycle

def alternate(xs1, xs2):
    xs1 = iter(xs1)
    xs2 = iter(xs2)
    for itr in cycle([xs1, xs2]):
        try:
            yield next(itr)
        except StopIteration:
            break

    yield from xs1
    yield from xs2

I prefer the above answer but here's a version that doesn't use any imports:我更喜欢上面的答案,但这是一个不使用任何导入的版本:

def alternate(xs1, xs2):
    xs1, xs2 = iter(xs1), iter(xs2)
    for e1, e2 in zip(xs1, xs2): # either xs1, xs2 exhausted by the end of this loop
        yield e1
        yield e2
    
    # pull remaining from the non-exhausted generator (if any)
    yield from xs1
    yield from xs2

If you want a version that doesn't transform the inputs into iterators and avoids using other libraries, the below would work, assuming both sequences are indexable.如果您想要一个不将输入转换为迭代器并避免使用其他库的版本,假设两个序列都是可索引的,则下面的方法将起作用。

def alternate2(xs1, xs2):
    n = min(len(xs1), len(xs2))
    for i in range(n):
        yield xs1[i]
        yield xs2[i]
    
    yield from xs1[n:]
    yield from xs2[n:]

It's going to be challenging to do away with iter() , as that's kind of what it's made for.取消iter()将具有挑战性,因为这就是它的目的。 If you can put up with that, this can be straightforward:如果你能忍受,这可能很简单:

def alternate(a, b):
    ia = iter(a)
    ib = iter(b)
    
    while True:
        try:
            yield next(ia)
            yield next(ib)
        except StopIteration:
            break
            
    yield from ia
    yield from ib
  1. Just try to yield the next item from each of the items in turn.只需尝试依次从每个项目中产生下一个项目。
  2. Once one of them is empty, yield the rest.一旦其中一个为空,则生成 rest。

This is slightly simpler than @wLui's:这比@wLui 的稍微简单一些:

def alternate(xs1, xs2):
     xs1 = iter(xs1)
     xs2 = iter(xs2)
     for pair in zip(xs1, xs2):
         yield from pair
     yield from xs1
     yield from xs2

(This is basically @chepner's answer from the comments, but I believe this is the right answer which deserves to appear here...) (这基本上是@chepner 从评论中得到的回答,但我相信这是正确的答案,值得出现在这里……)

You can use the roundrobin function from the more-itertools project (referenced from python docs ).您可以使用more-itertools 项目中的roundrobin function(引用自python 文档)。

I include it here for convenience:为了方便起见,我把它包括在这里:

def roundrobin(*iterables):
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
    # Recipe credited to George Sakkis
    num_active = len(iterables)
    nexts = cycle(iter(it).__next__ for it in iterables)
    while num_active:
        try:
            for next in nexts:
                yield next()
        except StopIteration:
            # Remove the iterator we just exhausted from the cycle.
            num_active -= 1
            nexts = cycle(islice(nexts, num_active))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 创建一个Python生成器,从两个大列表中生成有序的整数乘积 - Creating a Python generator that yields ordered products of integers from two large lists 编写一个 function 合并两个列表,交替两个列表中的元素。 Python代码 - Write a function that merges two lists, alternating elements from both lists. Python code 交流发电机 - Alternating Generator 如何将可迭代对象拆分为两个具有交替元素的列表 - How to split an iterable into two lists with alternating elements 生成器函数在列表和生成器表达式上的行为不同? - Generator function behaving differently on lists and generator expressions? 遍历生成器函数,该函数遍历两个非常大的列表列表 - Looping through a generator function that iterates through two very large lists of lists 将两个交替的XPath值匹配到一个列表中 - Matching two alternating XPath values into one list 创建一个生成器,从任意数量的内部生成器中产生值 - Create a generator that yields values from any number of inner generators 如何编写永不产生任何东西的 Python 生成器 function - How to write Python generator function that never yields anything Python以交替方式合并两个长度不等的列表 - Python combine two lists of unequal length in alternating fashion
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM