我无法理解"产量"在Python中使用递归函数

时间:2014-04-28 13:58:22

标签: python recursion

我试图理解“收益率”。

首先,我无法理解的代码如下。

def permutations(seq):
    if len(seq) <= 1: yield seq
    else:
        for perm in permutations(seq[1:]):
            for i in range(len(perm)+1):
                yield perm[i:] + seq[0:1] + perm[:i]

print list(permutations(['police', 'buffalo', 'fish']))

结果如下:

[['fish', 'buffalo', 'police'], ['buffalo', 'police', 'fish'], ['police', 'fish', 'buffalo'], ['buffalo', 'fish', 'police'], ['fish', 'police', 'buffalo'], ['police', 'buffalo', 'fish']]

我对“产量”的承诺水平只是用于生成。我可以理解下面的代码。

def reverse(data):
    for index in range(len(data) -1, -1, -1):
        yield data[index]

for char in reverse('golf'):
    print(char)

f
l
o
g

我的水平只是上面的理解..但是随着递归,我无法理解......请解释。谢谢

2 个答案:

答案 0 :(得分:3)

是的,产量适用于发电机。这意味着在调用时,它们会返回迭代器。生成器可以是递归的:它们将自己调用,获取迭代器并迭代它,在每次迭代时,它们可以根据自己的喜好产生尽可能多的元素。

在您的示例中,permutations是一个始终在列表上返回迭代器的生成器。

if len(seq) <= 1: yield seq

很简单:在简单的情况下,只需生成一个列表,seq本身。

for perm in permutations(seq[1:]):
        ...

表示“现在迭代不同的列表序列”,在这种情况下,它是第一个之后元素的所有排列的序列。在每次迭代中,我们都有一个嵌套循环,它将第一个元素插入到排列的每个位置并产生结果。

我希望有所帮助。这对我来说有点难,因为我不确切地知道你对此感到困惑。

更新: OP想知道为什么第一个结果的顺序相反。考虑一下:

yield perm[i:] + seq[0:1] + perm[:i]

对于第一个结果(i = 0),这相当于yield perm + seq[0:1] - 第一个元素被发送到产生列表的 end 。通过归纳,该结果与seq相反。如果您希望第一个结果为['police', 'buffalo', 'fish'],那么您可以执行以下操作:

yield perm[:i] + seq[0:1] + perm[i:]

答案 1 :(得分:1)

您提供的代码根据此算法运行:

  1. 如果序列有一个项目或为空,则唯一的排列是序列本身
  2. 将序列分隔为第一个元素及其所有剩余元素的列表
  3. 根据相同的算法,考虑序列其余部分的所有排列。对于每个排列:
    1. 对于排列中的每个位置:
      1. 在该位置拆分排列
      2. 在该分割中插入原始序列的第一个元素。这是原始序列的新排列。
  4. 完成。
  5. 原始代码在如何实现第3.1.2点方面略显奇怪。通过使用更多变量来更清楚地显示与算法的关系,我们可以更清楚地说明这一点 - 这假设您使用的是Python 3:

    def permutations(seq):
        if len(seq) <= 1: 
            yield seq 
        else:
            first, *rest = seq
            for perm in permutations(rest):
                for i in range(len(perm)+1):
                    before = perm[:i]
                    after = perm[i:]
                    yield after + [first] + before
    

    正如您所看到的,最后一行切换排列的开始和结束是没有实际意义的。它可以很容易地before + [first] + after,但作者决定不这样做。这并不影响算法的工作方式 - 它会找到 all 的顺序,包括镜像的顺序 - 但这意味着它产生它们的顺序可能有点奇怪。


    您也可以使用类似的递归生成器来实现reverse。在这种情况下,算法是:

    1. 如果序列只有一个项目或为空,则它是自己的反向
    2. 将序列拆分为第一个元素和所有剩余元素的列表
    3. 使用此算法反转剩余元素
    4. 产生最后一步结果的每一项
    5. 产生原始序列的第一个元素
    6. 在Python中,它看起来像这样:

      def reverse(seq):       如果len(seq)&lt; = 1:           返回seq

        first, *rest = seq
        for item in reverse(rest):
             yield item
        # Or you could use:
        #  yield from reverse(rest)
        # Instead of the above loop
        yield first