For循环使用递归调用的输出作为参数

时间:2018-08-12 14:53:41

标签: python recursion

当前正在学习Mark Lutz-学习Python 5e,并且有一个置换功能,我无法掌握,并希望获得一些指导。

def permute1(seq):
    if not seq:
        return [seq]
    else:
        res = []
        for i in range(len(seq)):
            rest = seq[:i] + seq[i+1:]
            for x in permute1(rest):
                res.append(seq[i:i+1] + x)
        return res

seq = (1,2,3)
permute1(seq)
  1. x使用哪个值。是来自上次permute1()通话吗?
  2. 在整个过程中,res语句顶部的else:为什么保持空白?这是一个可变的还是一成不变的问题? for循环实际上将其输出附加到哪个变量?
  3. for循环中重建序列之前清空序列的过程是否是我可以在其他地方查看的常见设计模式?

以下是permute1()的更有用的版本,其中所有变量都已打印出来。

def permute1(seq):
    if not seq:
        return [seq]
    else:
        res = []
        print('at the top seq = {} res = {},'.format(seq, res))
        for i in range(len(seq)):
            rest = seq[:i] + seq[i+1:]
            print('recursive call seq = {}, rest = {}, i = {}'.format(seq, rest, i))
            for x in permute1(rest):
                print('inside x = {}, rest = {}, seq = {}'.format(x, rest, seq))
                res.append(seq[i:i+1] + x)
        print('seq = {}; res = {}'.format(seq,res))
        return res

seq = (1,2,3)
permute1(seq)

1 个答案:

答案 0 :(得分:0)

递归函数调用只是另一个函数调用,实际上,这种调用没有什么特别的。您调用一个函数,然后在返回时使用该调用的结果。

通过递归,发生的是有一个堆栈调用,一个调用另一个调用另一个调用,直到最终一个函数返回结果,因此第一个外部调用可能需要等待一段时间才能继续给出给定的结果。

在您的示例中,permute1()总是返回一个列表(顶部是return [seq],或者是向下是return res),因此for x in ...会循环返回该结果(一旦递归调用返回),并使用结果中的每个元组在其前面添加一个单元组(res[i:i + 1]切片一个元组,使其仅包含单个元素,索引为i的元素)。

当您使用一个空的元组调用该函数时,该函数立即返回:

>>> permute1(())
[()]

这是一个没有递归调用的阶段,这一点很重要!递归就是这样!

另一个块使用seqrest = seq[:i] + seq[i+1:]创建了一个新的元素列表。这是一个删除了 one 元素的元组,该元素位于索引i

>>> seq
(1, 2, 3)
>>> seq[:1] + seq[2:]  # if i is 1, then..
(1, 3)

如果使用单个元素元组调用permute1(),则for i in range(len(seq))仅使用i = 0循环一次。这意味着将res设置为一个空元组(长度为1的元素被删除了1个元素的空元组为一个空元组),因此只进行了一次permute1(())调用。我们在上面看到,它仅返回[()],最后您将seq[i:i+1] + x添加到res,函数返回。

因此,其中包含单个元素的调用将进行一个递归调用,并返回具有单个元组的列表,该列表与开始时的基本相同:

>>> permute1((1,))
[(1,)]

对于2个元素,循环将迭代两次,并使用单个元素元组调用permute1()两次,即可完成上述操作。这两个调用各自导致一个包含1个元素的列表,因此您最终得到两个新的元组res,即输入元素的两个顺序:

>>> permute1((1,))
[(1, 2), (2, 1)]

您可以从那里推断3个元素的工作原理。

它可以通过传递级别信息来帮助缩进打印信息:

def permute1(seq, level=0):
    indent = '    ' * level
    p = lambda msg, *args: print(indent + msg.format(*args))
    p('permute1({}, {}) called', seq, level)
    if not seq:
        p('- end stage, seq is empty, returning [(),]')
        return [seq]
    else:
        p('- Looping {} times over {}', len(seq), seq)
        res = []
        for i in range(len(seq)):
            rest = seq[:i] + seq[i+1:]
            p('| Loop #{}, rest is: {}, seq[i] is {}, making recursive call', i + 1, rest, seq[i])
            for x in permute1(rest, level + 1):
                p(' + processing 1 result from recursive call, {}', x)
                res.append(seq[i:i+1] + x)
                p(' + res appended to, now {}', res)
            p('\ Loop #{} complete, res is: {}', i + 1, res)
        p('Function done, returning {}', res)
        return res

p lambda打印消息,其中level前面有4个空格;递归越深,则缩进越多。

这有点冗长,但是现在您可以看到发生了什么事情:

>>> permute1(seq)
permute1((1, 2, 3), 0) called
- Looping 3 times over (1, 2, 3)
| Loop #1, rest is: (2, 3), seq[i] is 1, making recursive call
    permute1((2, 3), 1) called
    - Looping 2 times over (2, 3)
    | Loop #1, rest is: (3,), seq[i] is 2, making recursive call
        permute1((3,), 2) called
        - Looping 1 times over (3,)
        | Loop #1, rest is: (), seq[i] is 3, making recursive call
            permute1((), 3) called
            - end stage, seq is empty, returning [(),]
         + processing 1 result from recursive call, ()
         + res appended to, now [(3,)]
        \ Loop #1 complete, res is: [(3,)]
        Function done, returning [(3,)]
     + processing 1 result from recursive call, (3,)
     + res appended to, now [(2, 3)]
    \ Loop #1 complete, res is: [(2, 3)]
    | Loop #2, rest is: (2,), seq[i] is 3, making recursive call
        permute1((2,), 2) called
        - Looping 1 times over (2,)
        | Loop #1, rest is: (), seq[i] is 2, making recursive call
            permute1((), 3) called
            - end stage, seq is empty, returning [(),]
         + processing 1 result from recursive call, ()
         + res appended to, now [(2,)]
        \ Loop #1 complete, res is: [(2,)]
        Function done, returning [(2,)]
     + processing 1 result from recursive call, (2,)
     + res appended to, now [(2, 3), (3, 2)]
    \ Loop #2 complete, res is: [(2, 3), (3, 2)]
    Function done, returning [(2, 3), (3, 2)]
 + processing 1 result from recursive call, (2, 3)
 + res appended to, now [(1, 2, 3)]
 + processing 1 result from recursive call, (3, 2)
 + res appended to, now [(1, 2, 3), (1, 3, 2)]
\ Loop #1 complete, res is: [(1, 2, 3), (1, 3, 2)]
| Loop #2, rest is: (1, 3), seq[i] is 2, making recursive call
    permute1((1, 3), 1) called
    - Looping 2 times over (1, 3)
    | Loop #1, rest is: (3,), seq[i] is 1, making recursive call
        permute1((3,), 2) called
        - Looping 1 times over (3,)
        | Loop #1, rest is: (), seq[i] is 3, making recursive call
            permute1((), 3) called
            - end stage, seq is empty, returning [(),]
         + processing 1 result from recursive call, ()
         + res appended to, now [(3,)]
        \ Loop #1 complete, res is: [(3,)]
        Function done, returning [(3,)]
     + processing 1 result from recursive call, (3,)
     + res appended to, now [(1, 3)]
    \ Loop #1 complete, res is: [(1, 3)]
    | Loop #2, rest is: (1,), seq[i] is 3, making recursive call
        permute1((1,), 2) called
        - Looping 1 times over (1,)
        | Loop #1, rest is: (), seq[i] is 1, making recursive call
            permute1((), 3) called
            - end stage, seq is empty, returning [(),]
         + processing 1 result from recursive call, ()
         + res appended to, now [(1,)]
        \ Loop #1 complete, res is: [(1,)]
        Function done, returning [(1,)]
     + processing 1 result from recursive call, (1,)
     + res appended to, now [(1, 3), (3, 1)]
    \ Loop #2 complete, res is: [(1, 3), (3, 1)]
    Function done, returning [(1, 3), (3, 1)]
 + processing 1 result from recursive call, (1, 3)
 + res appended to, now [(1, 2, 3), (1, 3, 2), (2, 1, 3)]
 + processing 1 result from recursive call, (3, 1)
 + res appended to, now [(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1)]
\ Loop #2 complete, res is: [(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1)]
| Loop #3, rest is: (1, 2), seq[i] is 3, making recursive call
    permute1((1, 2), 1) called
    - Looping 2 times over (1, 2)
    | Loop #1, rest is: (2,), seq[i] is 1, making recursive call
        permute1((2,), 2) called
        - Looping 1 times over (2,)
        | Loop #1, rest is: (), seq[i] is 2, making recursive call
            permute1((), 3) called
            - end stage, seq is empty, returning [(),]
         + processing 1 result from recursive call, ()
         + res appended to, now [(2,)]
        \ Loop #1 complete, res is: [(2,)]
        Function done, returning [(2,)]
     + processing 1 result from recursive call, (2,)
     + res appended to, now [(1, 2)]
    \ Loop #1 complete, res is: [(1, 2)]
    | Loop #2, rest is: (1,), seq[i] is 2, making recursive call
        permute1((1,), 2) called
        - Looping 1 times over (1,)
        | Loop #1, rest is: (), seq[i] is 1, making recursive call
            permute1((), 3) called
            - end stage, seq is empty, returning [(),]
         + processing 1 result from recursive call, ()
         + res appended to, now [(1,)]
        \ Loop #1 complete, res is: [(1,)]
        Function done, returning [(1,)]
     + processing 1 result from recursive call, (1,)
     + res appended to, now [(1, 2), (2, 1)]
    \ Loop #2 complete, res is: [(1, 2), (2, 1)]
    Function done, returning [(1, 2), (2, 1)]
 + processing 1 result from recursive call, (1, 2)
 + res appended to, now [(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2)]
 + processing 1 result from recursive call, (2, 1)
 + res appended to, now [(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]
\ Loop #3 complete, res is: [(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]
Function done, returning [(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]
[(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]