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