递归问题

时间:2016-01-26 11:50:31

标签: python recursion

假设:

numma = [1,2,3,4], n = 7

我希望以下代码返回:

[[1,2,3],[1,2,4]]

相反,它会返回一个包含以下四个副本的列表:

[1,2,4]

和六份副本:

[1,2,3,4]

该脚本旨在按顺序获取列表编号= numma的元素,并与它们形成一个新列表(this_chain),只要this_chain中元素的总和不超过n = 7。

当添加新元素会破坏该条件时,列表this_chain将附加到另一个列表(all_chains),然后该过程再次开始。如果原始列表(数字)用完元素,则当前this_chain将附加到all_chains并且脚本完成,返回名为all_chains的列表(列表)。

当我逐步运行它时,它遵循预期的行为,但是在到达return语句(步骤84)时,箭头向下移动半步,然后跳回到第一个语句,而不是返回all_chains并完成(for i in ...)。然后它返回并将当前this_chain的另一个副本附加到all_chains,依此类推,再执行66个步骤并返回上面提到的输出。

有人建议我迭代一个元组而不是列表,但是因为我想从迭代序列​​中删除元素,所以我看不出它是如何完成的。

我很困惑。我按照重要性顺序提出的四个问题如下:

  1. 为什么程序在到达退货声明后没有完成?我相信一个return语句总是会终止任何脚本。

  2. 鉴于上述行为,为什么脚本最终结束,而不是继续将有效列表的副本附加到名为all_chains的列表中?

  3. 为什么脚本会将无效元素附加到列表all_chains,即元素总和超过n = 7的列表?

  4. 为什么要删除(或修改)all_chains中的元素,以便它们不会出现在最终输出中,即使它们先前已附加到列表all_chains中了?

  5. 我的代码:

    def chain(numbers, n, all_chains=[], sum=0, this_chain=[]):
      for i in range(len(numbers)):
        if numbers[i] not in this_chain:
            sum += numbers[i]
            if sum <= n:
                this_chain.append(numbers[i])
                chain(numbers, n, all_chains, sum, this_chain)
            else:
                if this_chain not in all_chains:
                    all_chains.append(this_chain)
                    mocha = numbers[:]
                    mocha.remove(this_chain[-1])
                    chain(mocha, n, all_chains, sum=0, this_chain=[])
      all_chains.append(this_chain)
      return all_chains
    numma = [1,2,3,4]
    chain(numma, 7, chains=[], sum=0, this_chain=[])
    

1 个答案:

答案 0 :(得分:2)

  
      
  1. 为什么程序在达到退货声明后没有完成?   我相信一个return语句总会终止任何脚本。
  2.   

返回不会终止脚本;它离开当前例程并将控制权返回给调用它的那个例程。当你进行递归调用时,它只是将控制返回到调用它的“elder”版本。

  
      
  1. 鉴于上述行为,为什么最终会有脚本   结束,而不是继续将有效列表的副本附加到列表中   叫all_chains?
  2.   

它结束是因为所有活动的调用最终都会使它成为函数的底部,结束它们的执行。

  
      
  1. 为什么脚本会将无效元素追加到列表all_chains中,   也就是说,其元素总和超过n = 7的列表
  2.   

问题是,当你返回尝试添加4时,你认为的列表是[1,2],但它实际上是[1,2,3],因为......好吧,请看下一点。

  
      
  1. 为什么要删除(或修改)all_chains中的元素,所以   它们不在最终输出中,即使它们是   以前附加到列表all_chains?
  2.   

无论您将哪个附加到列表,都会附加对原始的引用,而不是本地副本。这来自于使用可变对象(如@tobias_k已经注意到的)。因此,当您将[1,2,3]附加到列表(多次)时,所有这些都是相同的对象。当您稍后错误地将4附加到列表中时(认为列表中只有[1,2]),您将 all 更改为[1,2,3,4]。我在下面的代码中解决了这个问题。

最后,由于缺少返回语句,您拥有所有这些额外的列表;而不是在你应该停止的时候,你会通过功能的其余部分,拨打更多电话,找到相同解决方案的更多副本。

我在代码中添加了一些内容,以便您可以查看执行情况。最重要的是,我添加了一些丑陋但有用的 print 语句来跟踪执行情况。我有以更易读的形式处理缩进,计数和参数打印的模块,您可以尝试查看最易读的内容。

我还将您的直接附加操作替换为副本,这样您就可以更好地区分问题并从错误中了解更多信息。

请记住,良好的选择来自经验。 经验来自糟糕的选择。

import copy

invocation = 0

def chain(numbers, n, all_chains=[], sum=0, this_chain=[]):
    global invocation
    invocation += 1
    local_id = invocation
    indent = "  "

    print indent*local_id, local_id, "ENTER", "\tsum", sum, "\tthis", this_chain, "\tall", all_chains
    for i in range(len(numbers)):
        if numbers[i] not in this_chain:
            sum += numbers[i]
            if sum <= n:
                print indent*local_id, local_id, "append to this", sum, this_chain, numbers[i]
                this_chain.append(numbers[i])
                chain(numbers, n, all_chains, sum, this_chain)
            else:
                if this_chain not in all_chains:
                    print indent*local_id, local_id, "append to all 1", this_chain
                    all_chains.append(copy.copy(this_chain))
                    mocha = numbers[:]
                    mocha.remove(this_chain[-1])
                    chain(mocha, n, all_chains, sum=0, this_chain=[])
    print indent*local_id, local_id, "append to all 2", this_chain
    all_chains.append(copy.copy(this_chain))
    print indent*local_id, local_id, "LEAVE", all_chains
    return all_chains

numma = [1, 2, 3, 4]
result = chain(numma, 7)
print
for x in result:
    print x

执行追踪:

   1 ENTER  sum 0   this []     all []
   1 append to this 1 [] 1
     2 ENTER    sum 1   this [1]    all []
     2 append to this 3 [1] 2
       3 ENTER  sum 3   this [1, 2]     all []
       3 append to this 6 [1, 2] 3
         4 ENTER    sum 6   this [1, 2, 3]  all []
         4 append to all 1 [1, 2, 3]
           5 ENTER  sum 0   this []     all [[1, 2, 3]]
           5 append to this 1 [] 1
             6 ENTER    sum 1   this [1]    all [[1, 2, 3]]
             6 append to this 3 [1] 2
               7 ENTER  sum 3   this [1, 2]     all [[1, 2, 3]]
               7 append to this 7 [1, 2] 4
                 8 ENTER    sum 7   this [1, 2, 4]  all [[1, 2, 3]]
                 8 append to all 2 [1, 2, 4]
                 8 LEAVE [[1, 2, 3], [1, 2, 4]]
               7 append to all 2 [1, 2, 4]
               7 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4]]
             6 append to all 2 [1, 2, 4]
             6 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4]]
           5 append to all 2 [1, 2, 4]
           5 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 4]]
         4 append to all 2 [1, 2, 3]
         4 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 3]]
       3 append to all 2 [1, 2, 3]
       3 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 3], [1, 2, 3]]
     2 append to this 7 [1, 2, 3] 4
                   9 ENTER  sum 7   this [1, 2, 3, 4]   all [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 3], [1, 2, 3]]
                   9 append to all 2 [1, 2, 3, 4]
                   9 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 3], [1, 2, 3], [1, 2, 3, 4]]
     2 append to all 2 [1, 2, 3, 4]
     2 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 3], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4]]
   1 append to all 2 [1, 2, 3, 4]
   1 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 3], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]

[1, 2, 3]
[1, 2, 4]
[1, 2, 4]
[1, 2, 4]
[1, 2, 4]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 4]
[1, 2, 3, 4]