何が起こっているのかを理解するために、いくつかの連続的に増加する の値に対するジェネレーターの出力を出力すると役立ちますn。
>>> for i in range(5):
print(list(gen(i)))
[()]
[(1,)]
[(1, 1), (2,)]
[(1, 1, 1), (1, 2), (3,)]
[(1, 1, 1, 1), (1, 1, 2), (2, 2), (1, 3), (4,)]
各シーケンスの計算は、その前のシーケンスに基づいています。これがどのように機能するかを行ごとに説明します。
if n == 0:
yield ()
return
ジェネレーターの最初の数行は、nがゼロの基本ケース用です。このreturnステートメントは、残りのコードを続行するのではなく、空のタプルを生成した後に終了します。
for p in gen(n-1):
これは、関数の再帰的なステップです。はジェネレーターなのでgen、ループで実行されるため、残りのコードはp、再帰の結果から異なる値を取得して繰り返し実行されます。
yield (1, ) + p
これは重要な行です。1これが行うことは、 aを value の先頭に連結することによって形成された新しいタプルをジェネレーターから生成することですp。が空のタプルの場合p、これは 1 タプルになります。pすでにいくつかの値がある場合、タプルは長くなります。
if p and (len(p)<2 or p[1] > p[0]):
これは複雑な条件です。2 番目のチェックは、最初の部分がすでに空であることを除外しているため、len(p)<2実際には と書くことができます。len(p)==1p andp
p渡すために持つことができる値には 2 種類あります。p値が 1 つだけであるか、複数の値があり、最初の値が 2 番目の値よりも小さいかのいずれかです。合格しますが、(3,)合格しません。(1,2)(1,1,1)
yield (p[0] + 1, ) + p[1:]
これは別のタプル連結です。の最初の値を 1 増やしてから、 (スライスを使用して)pの残りの値と連結します。pそう(3,)なり(4,)ます (スライスが空の状態で)、(1,2)なります(2,2)など。
それでは、上記の結果を見てみましょう。
[()]
0にn等しい場合、基本ケースがヒットし、単一の空のタプルが生成されます。
[(1,)]
1にn等しい場合、ループは 1 回実行(1,)され、空のタプルに連結され、 1-tuple が生成され(1,)ます。が空であるため、条件の最初の部分はif渡されません。p
[(1,1), (2,)]
2にn等しい場合、ループは 1 回だけ実行されます。まず、それ自体を(1,1)連結して を取得します。しかし今回は、値が 1 つしかないため、ブロックが実行されます。したがって、それも得られます。そのスライス部分は空のタプルですが、最初の部分は.(1,)(1,1)ifp(p[0]+1,) + p[1:](2,)
[(1,1,1), (1,2), (3,)]
これで、ループが複数回実行されるようになりました。(1,)ただし、最初は の前に別のものを連結しますが(1,1)、ifステートメントの条件が満たされないため、それがすべてです。ただし、2 番目のパスでは、2 つの値が生成されます。一度連結さ(1,)れて(2,)から にインクリメント(2,)され(3,)ます。
[(1, 1, 1, 1), (1, 1, 2), (2, 2), (1, 3), (4,)]
これはあなたに任せます。上記の場合と同様に、pは呼び出し ( ) の前のレベルからの各値を受け取り、(1,1,1), (1,2), (3,)各値に対して 1 つまたは 2 つの新しい結果を生成します。