我已经实现在Python发电的扫描仪,一个标记化串入的形式(标记类型,标记的值)的元组:
for token in scan("a(b)"):
print token
将打印
("literal", "a")
("l_paren", "(")
...
接下来的任务意味着解析符号流和为此,我需要能够从当前看未来一个项目不向前移动指针为好。 该迭代器和发电机不提供一次性项目的完整序列,但每个项目根据需要使向前看符号有点棘手相比列表,因为下一个项目不知道,除非事实__next__()
被调用。
可以直接实现基于发电机前瞻的看是什么样的? 目前我使用这意味着列清单离开发生器的解决方法:
token_list = [token for token in scan(string)]
先行那么容易被这样的事情来实现:
try:
next_token = token_list[index + 1]
except: IndexError:
next_token = None
当然,这只是正常工作。 但以为结束了,我的第二个问题是:是否有实拍的点scan()
发电机摆在首位?
Answer 1:
你可以写缓冲来自发电机项目的某编号的包装,并提供了一个前瞻()函数在这些缓冲项目偷看:
class Lookahead:
def __init__(self, iter):
self.iter = iter
self.buffer = []
def __iter__(self):
return self
def next(self):
if self.buffer:
return self.buffer.pop(0)
else:
return self.iter.next()
def lookahead(self, n):
"""Return an item n entries ahead in the iteration."""
while n >= len(self.buffer):
try:
self.buffer.append(self.iter.next())
except StopIteration:
return None
return self.buffer[n]
Answer 2:
相当不错的答案在那里,但我最喜欢的方法是使用itertools.tee
-给定一个迭代器,它返回两个(或更多,如果需要)可以独立前进。 它缓冲内存一样多,需要的(即不是很多,如果迭代器不从对方得到很“合拍”)。 例如:
import itertools
import collections
class IteratorWithLookahead(collections.Iterator):
def __init__(self, it):
self.it, self.nextit = itertools.tee(iter(it))
self._advance()
def _advance(self):
self.lookahead = next(self.nextit, None)
def __next__(self):
self._advance()
return next(self.it)
你可以用任何迭代器这个类,然后使用.lookahead
包装的属性知道将来要返回的下一个项目将是。 我喜欢把所有真正的逻辑itertools.tee,只是提供这本薄薄的胶水 - !)
Answer 3:
这不是漂亮,但是这可能会做你想要什么:
def paired_iter(it):
token = it.next()
for lookahead in it:
yield (token, lookahead)
token = lookahead
yield (token, None)
def scan(s):
for c in s:
yield c
for this_token, next_token in paired_iter(scan("ABCDEF")):
print "this:%s next:%s" % (this_token, next_token)
打印:
this:A next:B
this:B next:C
this:C next:D
this:D next:E
this:E next:F
this:F next:None
Answer 4:
下面是允许单个项目被发送回发生器的示例
def gen():
for i in range(100):
v=yield i # when you call next(), v will be set to None
if v:
yield None # this yields None to send() call
v=yield v # so this yield is for the first next() after send()
g=gen()
x=g.next()
print 0,x
x=g.next()
print 1,x
x=g.next()
print 2,x # oops push it back
x=g.send(x)
x=g.next()
print 3,x # x should be 2 again
x=g.next()
print 4,x
Answer 5:
通过构造一个简单的前瞻包装itertools.tee :
from itertools import tee, islice
class LookAhead:
'Wrap an iterator with lookahead indexing'
def __init__(self, iterator):
self.t = tee(iterator, 1)[0]
def __iter__(self):
return self
def next(self):
return next(self.t)
def __getitem__(self, i):
for value in islice(self.t.__copy__(), i, None):
return value
raise IndexError(i)
使用类来包装现有的迭代或迭代器。 然后,您可以重复使用正常或旁边 ,你可以使用索引查找预计。
>>> it = LookAhead([10, 20, 30, 40, 50])
>>> next(it)
10
>>> it[0]
20
>>> next(it)
20
>>> it[0]
30
>>> list(it)
[30, 40, 50]
要运行Python 3下这段代码,简单地改变未来的方法来__next__。
Answer 6:
既然你说你是标记化的字符串,而不是一个一般的迭代,我建议的只是扩大你的分词器返回一个3元组最简单的办法: (token_type, token_value, token_index)
其中token_index
是令牌的字符串中的索引。 然后你可以看向前,向后,或其他地方的的字符串中。 只是不要走过去的结束。 最简单,最灵活的解决方案,我认为。
此外,您不需要使用列表理解来从发电机的列表。 只需调用列表()构造函数就可以了:
token_list = list(scan(string))
Answer 7:
保罗是一个很好的答案。 任意前瞻类为基础的方法可能看起来是这样的:
class lookahead(object):
def __init__(self, generator, lookahead_count=1):
self.gen = iter(generator)
self.look_count = lookahead_count
def __iter__(self):
self.lookahead = []
self.stopped = False
try:
for i in range(self.look_count):
self.lookahead.append(self.gen.next())
except StopIteration:
self.stopped = True
return self
def next(self):
if not self.stopped:
try:
self.lookahead.append(self.gen.next())
except StopIteration:
self.stopped = True
if self.lookahead != []:
return self.lookahead.pop(0)
else:
raise StopIteration
x = lookahead("abcdef", 3)
for i in x:
print i, x.lookahead
Answer 8:
我怎么会写简明扼要它,如果我只需要向前看的1元的价值:
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>
文章来源: Using lookahead with generators