歩留まりを使用した再帰


82

再帰と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ループを使用してこれを実行できることを知っています。

def infinity(start):
    while True:
        yield start
        start += 1

これを再帰的に実行できるかどうかを知りたいだけです。


私がしばらく前に提起したこの質問に対する良い答えについては、[ここ] [1]を参照してください。[1]:stackoverflow.com/questions/5704220/...
sizzzzlerz

注:これを行う適切な方法はitertools.count、ループベースまたはその他の独自のソリューションをロールするのではなく、使用することです。
Petr Viktorin 2012年

8
@PetrViktorinこれは単なる例であり、無限の数を生成することは実際の問題ではありません
juliomalegria 2012年

回答:


154

はい、あなたはこれを行うことができます:

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は、関数本体を実際に実行したり、何も生成したりせずに、新しいジェネレーターを作成するだけです。

詳細については、PEP380を参照してください。


12
しかしyield from、再帰の制限はまだあるようです:(
Jo So

2
再帰制限は常にあります
ラジコン

12

場合によっては、ジェネレーターに再帰ではなくスタックを使用する方が望ましい場合があります。スタックと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'])

これは万能の解決策ではありませんが、一部のジェネレーターでは、再帰の代わりにスタック処理を使用すると、良い結果が得られる場合があります。


3
いい答えだ!Python 2.7のyieldは実際には再帰で使用できませんが、スタックを手動で管理することで同じ効果を得ることができます。
00prometheus 2017年

0
def lprint(a):
    if isinstance(a, list):
        for i in a:
            yield from lprint(i)
    else:
        yield a

b = [[1, [2, 3], 4], [5, 6, [7, 8, [9]]]]
for i in lprint(b):
    print(i)

bですか?コードのみの答えを残ししないようにしてください...少し明確化や説明は文脈で物事を入れて、より良いあなたの答えを理解するのに役立ちます
Tomerikoo

I LPRINT()内用:プリント(I)
ЮрийБлинков

より明確になるように答えを編集してみませんか?edit回答の下にある小さなタグをクリックするか、ここをクリックしてください。また、私はどのように、なぜこの解く問題について少し説明を追加しようと言ったように
Tomerikoo

-3

したがって、基本的には、関数を再帰的に呼び出す必要がある場所にforループを追加する必要があります。これはPython2.7に適用されます。


1
この答えは、より詳細な情報を必要とするが、それはスヴェンMarnachの受け入れ答えに沿ったもので、実際にある、コードの彼の最初のビットを参照してください...
Coffee_Table
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.