如果给定两个相同长度的列表,如何高效地找到这些列表对应元素第一次不相等的位置?我需要索引或两个不相等的元素。
我想知道是否存在简洁的“Python风格”的解决方案,而不需要明显地对列表进行迭代。
您无法避免对列表的迭代,但可以使用推导式来实现,并获得一种优美的解决方案:
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 )
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函数仅选择某些解决方案。
一种解决第一个问题的功能性方法:返回第一个不匹配项的索引
>>> from operator import ne
>>> from itertools import compress, count
>>> a = [1, 2, 4, 3]
>>> b = [1, 2, 3, 4]
>>> next(compress(count(), map(ne, a, b)))
2
针对第二个问题,采用功能性方法,返回第一个不匹配的一对而非索引
>>> from operator import ne
>>> from itertools import compress
>>> a = [1, 2, 4, 3]
>>> b = [1, 2, 3, 4]
>>> next(compress(zip(a, b), map(ne, a, b)))
(4, 3)
https://docs.python.org/zh-cn/3/library/itertools.html#itertools.compress
生成一个迭代器,从数据中过滤元素,仅返回那些对应选择器的元素计算结果为
True
的元素。在数据或选择器迭代器已耗尽时停止。大致等效于:
def compress(data, selectors):
# compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F
return (d for d, s in zip(data, selectors) if s)
自版本3.1起新增功能。
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
是 l1
和 l2
第一个不同的索引。
试试这个:
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
同时,在下一个命令中允许有额外的默认参数。由于过滤器(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'
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'
enumerate(zip(a, b))
的可能性吗? - Jon Clements