【问题标题】:Python: using a recursive algorithm as a generatorPython:使用递归算法作为生成器
【发布时间】:2016-08-25 08:01:15
【问题描述】:

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

这是一个例子。假设我们想用递归函数计算一个字符串的所有排列。下面的朴素算法接受一个额外的参数“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

这段代码工作(函数的行为就像一个空的生成器)。

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

【问题讨论】:

    标签: python recursion generator


    【解决方案1】:
    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
    

    【讨论】:

    • 在 Python 3.4 中,可以将最后两行替换为yield from getPermutations(string[:i] + string[i+1:]),这样在很多方面效率更高!
    • 您仍然需要以某种方式构建结果。使用 yield from 将需要您使用累加器参数 (prefix)。
    • 建议:定义另一个返回 string[i],string[:i]+string[i+1:] 对的生成器。那么它将是:for letter,rest in first_letter_options(string): for perm in getPermuations(rest): yield letter+perm
    【解决方案2】:

    这避免了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 允许我们通过简单地yielding 它来继续在另一个生成器中进行,而不是手动遍历它并yielding 每个项目。


    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])
    

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        猜你喜欢
        • 2014-04-07
        • 2021-05-22
        • 2016-05-03
        • 2012-10-24
        • 2019-10-18
        • 1970-01-01
        • 2021-11-12
        • 2013-09-16
        相关资源
        最近更新 更多