5

给定两个相同长度的列表,如何有效地找到这些列表的对应元素不相等的第一个位置?基本上我需要索引或两个不相等的元素。

我很感兴趣是否存在一些简洁的“pythonic”解决方案,而列表没有明显的显式迭代。

4

6 回答 6

5

您无法避免对列表的迭代,但您可以通过理解来做到这一点并获得一个优雅的解决方案:

next( (idx, x, y) for idx, (x, y) in enumerate(zip(list1, list2)) if x!=y )

如果你喜欢不那么以单线为中心的东西,你可以像这样拆分它

coupled_idx = enumerate(zip(list1, list2))
res = next( idx for idx, (x, y) in coupled_idx if x!=y )

编辑:

另外,如果您需要检查两个列表可以完全相等的情况,您可以在下一个函数中添加第二个参数,告诉它如果没有找到索引则返回什么。最常见的选项是返回 None:

coupled_idx = enumerate(zip(list1, list2))
res = next( (idx for idx, (x, y) in coupled_idx if x!=y), None )

请注意,您需要将生成器表达式括在括号之间,因为它不是此调用中函数的唯一参数。

为了增加一点乐趣,您还可以通过链接表达式来询问第 n 对不同的情侣。例如,这会为您提供直到第五对的所有夫妻(如果夫妻失踪,则填写无)

coupled_idx = enumerate(zip(list1, list2))
coupler = (idx for idx, (x, y) in coupled_idx if x!=y)
res = [ next(coupler, None) for _ in range(5) ]

编辑2:

这种解决方案实际上是通过 zip 函数创建两个列表的副本。如果您需要避免这种情况,您可以使用itertools 模块中的izip函数。

关于有趣的部分,您可以通过同一个模块的islice功能仅选择某些解决方案

于 2013-04-05T09:45:10.327 回答
2
>>> from operator import eq
>>> a = [1, 2, 4, 3]
>>> b = [1, 2, 3, 4]
>>> map(eq, a, b).index(False)
2
于 2013-04-05T10:51:10.193 回答
1
>>> from itertools import dropwhile
>>> a = [1, 2, 4, 3]
>>> b = [1, 2, 3, 4]
>>> next(dropwhile(lambda x: x[0] == x[1], zip(a, b)))
(4, 3)
于 2013-04-05T11:37:18.230 回答
0
In [1]: l1=[1,2,3]

In [2]: l2=[1,4,5]

In [4]: next(i for i, (el1, el2) in enumerate(zip(l1, l2)) if el1 != el2)
Out[4]: 1

这里,1是第一个索引wherel1l2differ。

于 2013-04-05T09:45:57.257 回答
0

尝试这个:

next(i for i, (el1,el2) in enumerate(zip(li1,li2)) if el1 != el2)

或者它的等效功能:

def first_diff(li1, li2):
    for i, (el1,el2) in enumerate(zip(li1,li2)):
        if el1 != el2:
            return i
    return False

一个例子

>>> li1 = range(32)
>>> li2 = range(32)
>>> li2[10] = 2
>>> next(i for i, (el1,el2) in enumerate(zip(li1,li2)) if el1 != el2)
10
>>> first_diff(li1, li2)
10
于 2013-04-05T09:52:17.493 回答
0

同时,下一个命令中允许有一个额外的默认参数。由于 filter 和 zip 是生成器,因此以下构造涉及两个列表的最小元素数,以确定两个列表在相同索引处定义但在该索引处具有不同值的第一个值,或者在不存在此类值时返回默认值。

只要可以找到差异,就会返回不同的值:

a=[1,2,3,4,5,6]

b=[1,2,7,4,'Hugo']

next(filter(lambda x: x[0]!=x[1], zip(a,b)),"Value that you choose to represent failure")

Out[91]: (3, 7)

当两个列表相等时,返回默认值:

b[2]=3; b[4]=5; b.append(6)

next(filter(lambda x: x[0]!=x[1], zip(a,b)),"Value that you choose to represent failure")

Out[93]: 'Value that you choose to represent failure'

当任何其他问题导致搜索失败时,将返回默认值:

a=[]

next(filter(lambda x: x[0]!=x[1], zip(a,b)),"Value that you choose to represent failure")

Out[95]: 'Value that you choose to represent failure'

当然,您可以将生成器分配给一个变量并重复使用 next 来查找第二个或其他事件。

a=[1,2]

b=['fred',1,2.2]

f=filter(lambda x: x[0]!=x[1], zip(a,b))

next(f,'error')

Out[110]: (1, 'fred')

next(f,'error')

Out[111]: (2, 1)

next(f,'error')

Out[112]: 'error'

您可以使用 None 作为默认值以静默失败或用于测试

next(f,None)

脱离列表末尾后的后续调用不会引发异常或错误。

next(f,'No more')

Out[114]: 'No more'
于 2020-09-21T18:10:07.260 回答