最近、重要な制約を持つ特定のシーケンスを生成する関数を作成しました。この問題は、自然な再帰的解決策によってもたらされました。比較的小さな入力であっても、シーケンスは数千であるため、リストにすべてのシーケンスを入力するのではなく、アルゴリズムをジェネレータとして使用することをお勧めします。
以下に例を示します。再帰関数を使用して文字列のすべての順列を計算するとします。次の単純なアルゴリズムは、追加の引数 'storage'を受け取り、見つかった場合は常に置換を追加します。
def getPermutations(string, storage, prefix=""):
if len(string) == 1:
storage.append(prefix + string) # <-----
else:
for i in range(len(string)):
getPermutations(string[:i]+string[i+1:], storage, prefix+string[i])
storage = []
getPermutations("abcd", storage)
for permutation in storage: print permutation
(非効率については気にしないでください。これは一例です。)
次に、関数をジェネレーターに変換します。つまり、ストレージリストに追加する代わりに置換を生成します。
def getPermutations(string, prefix=""):
if len(string) == 1:
yield prefix + string # <-----
else:
for i in range(len(string)):
getPermutations(string[:i]+string[i+1:], prefix+string[i])
for permutation in getPermutations("abcd"):
print permutation
このコードはnotを実行します(関数は空のジェネレーターのように動作します)。
何か不足していますか?上記の再帰的アルゴリズムをジェネレーターに変える方法はありますか反復アルゴリズムに置き換えることなく?
def getPermutations(string, prefix=""):
if len(string) == 1:
yield prefix + string
else:
for i in xrange(len(string)):
for perm in getPermutations(string[:i] + string[i+1:], prefix+string[i]):
yield perm
またはアキュムレーターなし:
def getPermutations(string):
if len(string) == 1:
yield string
else:
for i in xrange(len(string)):
for perm in getPermutations(string[:i] + string[i+1:]):
yield string[i] + perm
これはlen(string)
- deep再帰を回避し、一般にジェネレーター内部ジェネレーターを処理するための素晴らしい方法です:
from types import GeneratorType
def flatten(*stack):
stack = list(stack)
while stack:
try: x = stack[0].next()
except StopIteration:
stack.pop(0)
continue
if isinstance(x, GeneratorType): stack.insert(0, x)
else: yield x
def _getPermutations(string, prefix=""):
if len(string) == 1: yield prefix + string
else: yield (_getPermutations(string[:i]+string[i+1:], prefix+string[i])
for i in range(len(string)))
def getPermutations(string): return flatten(_getPermutations(string))
for permutation in getPermutations("abcd"): print permutation
flatten
を使用すると、別のジェネレーターを反復処理して各アイテムを手動でyield
ingするのではなく、単にyield
ingするだけで、別のジェネレーターで進行を続けることができます。
Python 3.3は yield from
構文の場合、サブジェネレーターへの自然な委任が可能になります。
def getPermutations(string, prefix=""):
if len(string) == 1:
yield prefix + string
else:
for i in range(len(string)):
yield from getPermutations(string[:i]+string[i+1:], prefix+string[i])
GetPermutationsの内部呼び出し-ジェネレーターでもあります。
def getPermutations(string, prefix=""):
if len(string) == 1:
yield prefix + string
else:
for i in range(len(string)):
getPermutations(string[:i]+string[i+1:], prefix+string[i]) # <-----
Forループでそれを繰り返す必要があります(@MizardXの投稿をご覧ください。