检查两台发电机的不同之处

时间:2015-03-24 21:49:42

标签: python functional-programming generator

假设我有两个生成器,为简单起见:

it1 = iter([1,2,3,4])
it2 = iter([1,2,10,20])

知道第一个差异(位置2)或者它们是否相等的最佳方法是什么?我想避免显式循环:

for pos, v1, v2 in enumerate(izip(it1, it2)):
   if v1 != v2: return pos
return None

并使用函数式编程:

try:
  return next(pos for pos, (v1, v2) in enumerate(izip(it1, it2)) if v1 != v2)
except StopIteration:
  return None

重点是我使用的解决方案是丑陋的,比显式循环更冗长,...

你能做得更好吗?

3 个答案:

答案 0 :(得分:3)

您可以通过为next提供默认值来简化第二个示例:

return next((pos for pos, (v1, v2) in enumerate(izip(it1, it2)) if v1 != v2), None)

来自docs

  

next(iterator[, default])

     

通过调用iterator方法从__next__()中检索下一个项目。 如果给出default,则迭代器返回时返回   筋疲力尽,否则会引发StopIteration

答案 1 :(得分:1)

作为替代答案,您可以使用numpy.where代替循环数组并使用enumerate

>>> import numpy as np
>>> def test(a,b):
...    try :
...         return np.where(a!=b)[0][0]
...    except IndexError:
...         return None
... 
>>> test(it1,it2)
>>> it1 = np.array([1,2,3,4])
>>> it2 = np.array([1,2,0,4])
>>> test(it1,it2)
2

next

>>> it1 = np.array([1,2,3,4,6])
>>> it2 = np.array([1,2,3,0,6])
>>> next(iter(np.where(it1!=it2)[0]),None)
3

同样对于generators,您可以使用numpy.fromiter将您的生成器转换为numpy数组!

一些基准:

:~$ python -m timeit "from itertools import izip;it1=[1,2,3,4];it2=[1,2,10,20];next((pos for pos, (v1, v2) in enumerate(izip(it1, it2)) if v1 != v2), None)"
100000 loops, best of 3: 2.77 usec per loop
:~$ python -m timeit "import numpy as np;it1=[1,2,3,4];it2=[1,2,10,20];next(iter(np.where(it1!=it2)[0]),None)"
100000 loops, best of 3: 2.02 usec per loop

答案 2 :(得分:0)

我一直在尝试内置filter,但可能python3 filterfalse可能更好。

filter(lambda x: x if len(set(x)) else None, zip(it1, it2))
>>> [(3, 10), (4, 20)]

但是,我们不会以这种方式得到差异的索引,而只是不同的项目。

所以我一直在考虑更多并检查related但实际上,OP希望索引不是结果。所以,修改了

filter(lambda x:x[0] if len(set(x[1])) > 1 else None, enumerate(zip(it1,it2)))
>>> [(2, (3, 10)), (3, (4, 20))]

当然这甚至没有接近上面的答案所以我不建议在生产代码中。只有好处?没有进口。以下是我的时间结果:

python -m timeit "import numpy as np;it1=[1,2,3,4];it2=[1,2,10,20];next(iter(np.where(it1!=it2)[0]),None)"
100000 loops, best of 3: 2.02 usec per loop
10 loops, best of 3: 13.1 usec per loop

python -m timeit "from itertools import izip;it1=[1,2,3,4];it2=[1,2,10,20];next((pos for pos, (v1, v2) in enumerate(izip(it1, it2)) if v1 != v2), None)"
100000 loops, best of 3: 16.6 usec per loop

python -m timeit "it1=[1,2,3,4];it2=[1,2,10,20];filter(lambda x:x[0] if len(set(x[1])) > 1 else None, enumerate(zip(it1,it2)))"
10000 loops, best of 3: 19.1 usec per loop