我正在做一些需要一个前瞻标记的解析。我想要的是一个快速函数(或类?),它将采用迭代器并将其转换为表单中的元组列表(token,lookahead),这样:
>>> a = ['a', 'b', 'c', 'd']
>>> list(lookahead(a))
[('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', None)]
基本上,这对于在这样的迭代器中展望是很方便的:
for (token, lookahead_1) in lookahead(a):
pass
尽管如此,我不确定在itertools中是否有这个技术或函数的名称已经可以做到这一点。有什么想法吗?
谢谢!
答案 0 :(得分:11)
如果您只是使用列表,有更简单的方法 - 请参阅Sven的回答。这是为通用迭代器执行此操作的一种方法
>>> from itertools import tee, izip_longest
>>> a = ['a', 'b', 'c', 'd']
>>> it1, it2 = tee(iter(a))
>>> next(it2) # discard this first value
'a'
>>> [(x,y) for x,y in izip_longest(it1, it2)]
# or just list(izip_longest(it1, it2))
[('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', None)]
以下是如你问题中的for循环中使用它的方法。
>>> it1,it2 = tee(iter(a))
>>> next(it2)
'a'
>>> for (token, lookahead_1) in izip_longest(it1,it2):
... print token, lookahead_1
...
a b
b c
c d
d None
最后,这是您正在寻找的功能
>>> def lookahead(it):
... it1, it2 = tee(iter(it))
... next(it2)
... return izip_longest(it1, it2)
...
>>> for (token, lookahead_1) in lookahead(a):
... print token, lookahead_1
...
a b
b c
c d
d None
答案 1 :(得分:8)
我同时喜欢Sven's和gnibbler's个答案,但出于某种原因,我喜欢推出自己的发电机。
def lookahead(iterable, null_item=None):
iterator = iter(iterable) # in case a list is passed
prev = iterator.next()
for item in iterator:
yield prev, item
prev = item
yield prev, null_item
测试:
>>> for i in lookahead(x for x in []):
... print i
...
>>> for i in lookahead(x for x in [0]):
... print i
...
(0, None)
>>> for i in lookahead(x for x in [0, 1, 2]):
... print i
...
(0, 1)
(1, 2)
(2, None)
编辑:Karl和ninjagecko提出了一个很好的观点 - 传入的序列可能包含None
,因此使用None
作为最终前瞻值可能会导致歧义。但是没有明显的替代方案;在许多情况下,模块级常量可能是最好的方法,但对于像这样的一次性函数可能有点过分 - 更不用说bool(object()) == True
这可能导致意外行为的事实。相反,我添加了一个null_item
参数,默认值为None
- 这样用户就可以传入任何有意义的参数,无论是一个简单的object()
哨兵,一个常数他们自己的创造,甚至是具有特殊行为的类实例。由于大部分时间None
是明显的,甚至可能是预期的行为,因此我将None
作为默认值。
答案 2 :(得分:6)
对列表a
执行此操作的常用方法是
from itertools import izip_longest
for token, lookahead in izip_longest(a, a[1:]):
pass
对于最后一个标记,您将获得None
作为预告标记。
如果您想避免a[1:]
引入的列表副本,可以改用islice(a, 1, None)
。对于任意迭代的略微修改,请参阅answer by gnibbler。对于一个简单易用的生成器函数,也适用于任意迭代,请参阅the answer by senderle。
答案 3 :(得分:2)
您可以在此处找到问题的答案:Using lookahead with generators。
答案 4 :(得分:1)
我认为所有这些答案都不正确,因为如果您的列表包含None
,它们会导致无法预料的错误。这是我的看法:
SEQUENCE_END = object()
def lookahead(iterable):
iter = iter(iterable)
current = next(iter)
for ahead in iter:
yield current,ahead
current = ahead
yield current,SEQUENCE_END
示例:
>>> for x,ahead in lookahead(range(3)):
>>> print(x,ahead)
0, 1
1, 2
2, <object SEQUENCE_END>
这个答案如何更好的例子:
def containsDoubleElements(seq):
"""
Returns whether seq contains double elements, e.g. [1,2,2,3]
"""
return any(val==nextVal for val,nextVal in lookahead(seq))
>>> containsDoubleElements([None])
False # correct!
def containsDoubleElements_BAD(seq):
"""
Returns whether seq contains double elements, e.g. [1,2,2,3]
"""
return any(val==nextVal for val,nextVal in lookahead_OTHERANSWERS(seq))
>>> containsDoubleElements([None])
True # incorrect!