使用前瞻与发电机(Using lookahead with generators)

2019-07-20 20:42发布

我已经实现在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