Python のジェネレータ (4) - 無限リスト のつづき
1. 例
例えば、「リストの要素を 2 倍したい」とする。
リスト内包表記や map 関数を使うなら、以下のように書ける。
L = [1,2,3,4,5] print [x*2 for x in L] print map(lambda e: e*2, L)
2. for ループ
単純に、for ループでリストを走査しながら、要素を2 倍するなら、
def double(L):
result = []
for e in L:
result.append(e*2)
return result
print double(L)
これをジェネレータで置き換える。
def gDouble(L):
for e in L:
yield e * 2
for e in gDouble(L):
print e
3. 再帰
再帰で書くと、
def rDouble(L):
if not L: return []
else:
return [L[0]*2] + rDouble(L[1:])
print rDouble(L)
これをジェネレータで置き換えてみる。ジェネレータは要素をいっぺんに返さず、処理した要素ごとに返すようにする。
def grDouble(L):
if not L: return
else:
yield L[0]*2
for g in grDouble(L[1:]):
yield g
for e in grDouble(L):
print e
ジェネレータを再帰的に呼出すには、再帰的に適用したい部分を for に投入し (next() が呼出されるため)、その要素を yield で返す。渡された引数 L の要素がなければ、何もせず return でジェネレータを終了する。
次のように書き直すこともできる。
def grDouble(L):
if L:
yield L[0]*2
for g in grDouble(L[1:]):
yield g
4. Composite
ジェネレータを再帰的に呼出すことに、何かメリットがあるのだろうか?
ツリー状のノードの要素を走査するためにジェネレータが使われている例 を目にした。これを見ると、Iterator パターン 、Visitor パターン を連想する。
試しに、Composite パターン のオブジェクトに対して、その要素を走査する関数をジェネレータで定義してみる。
class Component:
pass
class Composite(Component):
def __init__(self):
self.components = []
def add(self, component):
self.components.append(component)
return self
def __iter__(self):
return iter(self.components)
class Leaf(Component):
def __init__(self, val):
self.val = val
def gComposite(composite):
if isinstance(composite, Leaf):
yield composite.val
else:
for c in composite:
for g in gComposite(c):
yield g
c = Composite().add(
Leaf(100)).add(
Leaf(200)).add(
Composite().add(
Leaf(1000)).add(
Composite().add(
Leaf(10000)).add(
Leaf(2000))).add(
Leaf(300)))
for e in gComposite(c):
print e
Composite クラスの __iter__() メソッドの定義については、Python のイテレータ (3) を参照。
0コメント:
コメントを投稿