如何以python方式以任意顺序迭代两个列表

How to pythonically iterate two lists in an arbitrary order

我想以这样一种方式遍历两个列表,以便我可以从一个列表中获取任意数量的值并保持我在另一个列表中的位置。

我使用索引来存储每个列表中的当前位置,然后使用 while 循环遍历它们,但这绝对不是很 pythonic。

def alternate_iterate(a,b,cond=lambda x, y : x > y):
    pos_a = 0
    pos_b = 0
    retval = []

    while(True):

        if(pos_a == len(a) and pos_b == len(b)):
            break

        if(pos_a < len(a) and cond(a[pos_a],b[pos_b])):
            retval += [a[pos_a]]
            pos_a += 1
        elif(pos_b < len(b)):
            retval += [b[pos_b]]
            pos_b += 1

    return retval

#example usage
print(alternate_iterate(['abc','abcd','ab','abc','ab'],
                        ['xy','xyz','x','xyz'],
                        cond=lambda x,y: len(x) > len(y))

这应该打印 ['abc','abdc','xy','xyz','ab','abc','ab','x','xyz'],您没有完美的 1:1 交替顺序。元素的顺序和元素的类型应该只取决于 cond 被定义为什么。

将您的列表放入生成器中,然后您可以对每个列表调用 next 以获取下一个值。这个答案并不是一个完整的解决方案,只是为了展示生成器如何使用非常简单的 Pythonic 代码以任何顺序生成值:

agen = iter(a)
bgen = iter(b)
print next(agen) # 'a'
print next(bgen) # 1
print next(bgen) # 2
print next(agen) # 'b'

等等。

欢迎使用 Whosebug。总而言之,您似乎想要根据某个谓词的值从一个列表或另一个列表中获取一个值。您现有的逻辑似乎没有考虑到其中一个列表被耗尽的可能性,此时我假设您想要从另一个列表复制任何剩余的值。

您可以在列表上构建一个迭代器,然后使用next 函数获取select 连续列表元素,而不是使用索引值下一个值。

在那种情况下,您的逻辑最终会看起来像这样:

def alternate_iterate(a_lst, b_lst, cond=lambda x, y: x > y):
    a_iter = iter(a_lst)
    b_iter = iter(b_lst)
    a = next(a_iter)
    b = next(b_iter)
    ret = []
    while True:
        if cond(a, b):
            ret.append(a)
            try:
                a = next(a_iter)
            except StopIteration:
                ret.append(b)
                for x in b_iter:
                    ret.append(x)
                return ret
        else:
            ret.append(b)
            try:
                b = next(b_iter)
            except StopIteration:
                ret.append(a)
                for x in a_iter:
                    ret.append(x)
                return ret


print(alternate_iterate(['abc','abcd','ab','abc','ab'],
                        ['xy','xyz','x','xyz'],
                        cond=lambda x,y: len(x) > len(y)))

我得到的结果是

['abc', 'abcd', 'xy', 'xyz', 'ab', 'abc', 'ab', 'x', 'xyz']

这似乎是您所期望的。

在此类示例中经常出现的情况是,与处理 "happy path" 情况相比,您编写更多的逻辑来处理更罕见的极端情况(在这种情况下,一个列表或另一个变得耗尽)照常进行。

此版本仅使用迭代器懒惰地实现功能(Pythonic):

a = ['abc','abcd','ab','abc','ab']
b = ['xy','xyz','x','xyz']

cond=lambda x,y: len(x) > len(y)

def alternate_iterate(a, b, cond):
    a, b = iter(a), iter(b)

    def _return_rest():
        def _f(val, it):
            yield val
            yield from it
        return _f

    v1, v2 = next(a, _return_rest), next(b, _return_rest)

    while True:
        if v1 is _return_rest:
            yield from v1()(v2, b)
            break

        if v2 is _return_rest:
            yield from v2()(v1, a)
            break

        if cond(v1, v2):
            yield v1
            v1 =  next(a, _return_rest)
        else:
            yield v2
            v2 = next(b, _return_rest)

print(list(alternate_iterate(a, b, cond)))

打印:

['abc', 'abcd', 'xy', 'xyz', 'ab', 'abc', 'ab', 'x', 'xyz']

更 Pythonic 的方式通常是根本不使用索引,最好不要使用异常作为控制 "intended" 程序逻辑的手段。您还应该避免不必要的括号。

以下是使用迭代器的方法:

def merge(a, b, cond=lambda x, y : x < y):
    Done           = []
    iterA, iterB   = iter(a), iter(b)
    valueA, valueB = next(iterA, Done), next(iterB, Done)
    result         = []
    while not(valueB is Done and valueA is Done):
        if valueB is Done or valueA is not Done and cond(valueA, valueB):
            result.append(valueA)
            valueA = next(iterA, Done)
        else:
            result.append(valueB)
            valueB = next(iterB, Done)
    return result

这有一个额外的好处,可以使函数有效地使用任何可迭代数据作为参数。

例如:

print(merge(range(5, 10), range(7, 15)))

# [5, 6, 7, 7, 8, 8, 9, 9, 10, 11, 12, 13, 14]

它还可以轻松地为延迟遍历创建函数的迭代器版本:

def iMerge(a, b, cond=lambda x, y : x < y):
    Done           = []
    iterA, iterB   = iter(a), iter(b)
    valueA, valueB = next(iterA, Done), next(iterB, Done)
    while not(valueB is Done and valueA is Done):
        if valueB is Done or valueA is not Done and cond(valueA, valueB):
            yield valueA
            valueA = next(iterA ,Done)
        else:
            yield valueB
            valueB = next(iterB, Done)

编辑None 更改为 Done 以使函数支持 None 作为输入列表中的合法值。