ジェネレータで先読みを使用する 質問する

ジェネレータで先読みを使用する 質問する

私はPythonでジェネレータベースのスキャナを実装しました。これは文字列を次の形式のタプルにトークン化します。(トークンタイプ、トークン値):

for token in scan("a(b)"):
    print token

印刷する

("literal", "a")
("l_paren", "(")
...

次のタスクはトークン ストリームを解析することを意味し、そのためには、ポインターを先に動かすことなく、現在の項目から 1 つ先の項目を参照できる必要があります。イテレータとジェネレーターは、項目の完全なシーケンスを一度に提供するのではなく、必要に応じて各項目を提供するため、リストと比較して先読みが少し難しくなります。これは、__next__()が呼び出されるまで次の項目がわからないためです。

ジェネレータベースの先読みの簡単な実装はどのようなものになるでしょうか? 現在、ジェネレータからリストを作成する回避策を使用しています。

token_list = [token for token in scan(string)]

先読みは次のように簡単に実装できます。

try:
    next_token = token_list[index + 1]
except: IndexError:
    next_token = None

もちろん、これで問題なく動作します。しかし、よく考えてみると、2 番目の疑問が浮かびます。そもそも、scan()ジェネレータを作成する意味は本当にあるのでしょうか?

ベストアンサー1

かなり良い回答ですが、私のお気に入りのアプローチはitertools.tee、イテレータを指定すると、独立して進めることができる 2 つ (または要求に応じてそれ以上) を返すことです。必要な分だけメモリにバッファリングします (つまり、イテレータが互いにあまり「ずれ」ていなければ、それほど多くはありません)。例:

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 に任せて、この薄い接着剤だけを提供するのが好きです!-)

おすすめ記事