再帰とyield
ステートメントを混在させる方法はありますか?たとえば、無限数ジェネレーター(再帰を使用)は次のようになります。
def infinity(start):
yield start
# recursion here ...
>>> it = infinity(1)
>>> next(it)
1
>>> next(it)
2
私は試した:
def infinity(start):
yield start
infinity(start + 1)
そして
def infinity(start):
yield start
yield infinity(start + 1)
しかし、それらのどれも私が望むことをしませんでした。最初のものはstart
を生成した後に停止し、2番目のものはstart
を生成し、その後ジェネレーターを停止しました。
注: while-loopを使用してこれを行うことができることを知っています:
def infinity(start):
while True:
yield start
start += 1
これが再帰的に実行できるかどうかを知りたいだけです。
はい、これを行うことができます:
def infinity(start):
yield start
for x in infinity(start + 1):
yield x
ただし、最大再帰深度に達するとエラーになります。
Python 3.3から、使用できるようになります
def infinity(start):
yield start
yield from infinity(start + 1)
ループすることなく、ジェネレータ関数を再帰的に呼び出す場合、またはyield from
- ing、あなたがすることは、実際に関数本体を実行したり、何も生成したりすることなく、新しいジェネレータを構築することです。
詳細については、 PEP 38 を参照してください。
場合によっては、ジェネレーターに対して再帰ではなくスタックを使用することが望ましい場合があります。スタックとwhileループを使用して再帰的なメソッドを書き換えることができるはずです。
コールバックを使用し、スタックロジックを使用して書き換え可能な再帰メソッドの例を次に示します。
def traverse_tree(callback):
# Get the root node from somewhere.
root = get_root_node()
def recurse(node):
callback(node)
for child in node.get('children', []):
recurse(child)
recurse(root)
上記のメソッドは、各ノードが子ノードを含む可能性のあるchildren
配列を持つノードツリーを走査します。各ノードが検出されると、コールバックが発行され、現在のノードが渡されます。
この方法をこのように使用して、各ノードのプロパティを出力することができます。
def callback(node):
print(node['id'])
traverse_tree(callback)
代わりにスタックを使用し、ジェネレータとしてトラバーサルメソッドを記述します
# A stack-based alternative to the traverse_tree method above.
def iternodes():
stack = [get_root_node()]
while stack:
node = stack.pop()
yield node
for child in reversed(node.get('children', [])):
stack.append(child)
(元と同じ走査順序が必要な場合は、スタックに追加された最初の子が最後にポップされるため、子の順序を逆にする必要があることに注意してください。)
これで、上記のtraverse_tree
と同じ動作を得ることができますが、ジェネレーターを使用します:
for node in iternodes():
print(node['id'])
これは万能のソリューションではありませんが、一部のジェネレーターでは、スタック処理を再帰に置き換えてニースの結果が得られる場合があります。
def lprint(a):
if isinstance(a, list):
for i in a:
yield from lprint(i)
else:
yield a
a = [[1, [2, 3], 4], [5, 6, [7, 8, [9]]]]
for i in lprint(b):
print(i)