101

最近我写了一个函数来生成具有非平凡约束的某些序列。问题来自一个自然的递归解决方案。现在碰巧的是,即使对于相对较小的输入,序列也是数千个,因此我更愿意使用我的算法作为生成器,而不是使用它来填充所有序列的列表。

这是一个例子。假设我们想用递归函数计算一个字符串的所有排列。以下朴素算法采用额外的参数“存储”,并在找到时附加一个排列:

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

此代码不起作用该函数的行为类似于一个空生成器)。

我错过了什么吗?有没有办法将上述递归算法变成生成器而不用迭代算法替换它

4

3 回答 3

118
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
于 2008-10-29T23:53:47.840 回答
29

这避免了len(string)-deep 递归,并且通常是处理 generators-inside-generators 的好方法:

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允许我们通过简单地 ing 它来继续在另一个生成器中取得进展yield,而不是遍历它并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])
于 2008-10-31T00:05:38.003 回答
20

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 帖子,它让我领先几秒钟!)

于 2008-10-29T23:54:51.923 回答