如何在不靠近计算机的情况下理解功能的作用?

时间:2017-01-25 19:21:23

标签: python recursion

我正在学习考试并遇到了这个问题。



def subsets(lst):
   if len(lst)==0:
       yield []
   else:
       for tail in subsets(lst[1:]):
           yield tail
           yield [lst[0]]+tail

print(list(subsets([1,5,3])))




我们应该在不使用计算机的情况下写下输出内容。

现在这是一个迭代器,在函数之前放置列表会将程序产生的任何内容放入列表中。

我进入第三次递归并且我很难跟踪将要发生的事情,因为我们正在对递归的结果运行循环。

是否有任何技巧/方法来绘制某种树/表格以帮助我更好地理解这段代码的输出?

3 个答案:

答案 0 :(得分:2)

程序生成所有子集。这是因为在for循环中,它"发出"尾部的所有subsets,并且有一个版本的" head"没有"。

我会 - 为了分析它的功能 - 进行相反的分析。很明显,对于每个递归调用,我们都会删除一个元素:" head"可以这么说。当没有磁头时,递归结束:在这种情况下,我们发出空列表。

现在我们向上移动一级:如果我们用一个元素提供一个列表怎么办?递归调用显然会发出空列表(正如我们刚刚演示的那样),这里我们发出两个版本:一个带头,一个带头。因此,如果我们提供[a],我们会得到两个版本:[][a]

现在归纳地可以说,当我们获得列表S的所有子集L=[x2,x3,x4,...,xn]的列表时,L'=[x1,x2,x3,...,xn]的子集列表是子集列表{{1 } S以及我们添加L的子集列表。

关于事物的顺序,因为在每个递归x1循环中,我们首先for没有" head"的版本,很明显将会产生空列表第一。接下来,既然我们处于领先地位,那就是真正的头脑。下一个元素将是第二个元素所在的元素,而不是其他元素。因此它就像二进制计数。对于列表yield

[x1,x2,x3,x4]

如果你想要相反的方式,那么你应该在一个单独的binary result 0000 [] 0001 [x1] 0010 [x2] 0011 [x1,x2] 0100 [x3] 0101 [x1,x3] 0110 [x2,x3] 0111 [x1,x2,x3] 1000 [x4] 1001 [x1,x4] 1010 [x2,x4] 1011 [x1,x2,x4] 1100 [x3,x4] 1101 [x1,x3,x4] 1110 [x2,x3,x4] 1111 [x1,x2,x3,x4] 循环中不用和使用它:

for

答案 1 :(得分:0)

我认为当你完全有能力自己编写代码时,你可以参加考试。尝试自己编写代码,放置"打印"在函数中看看到底发生了什么。

答案 2 :(得分:0)

首先,我确定了每次迭代中函数所需的所有局部变量。这包括参数,本地创建的变量,以及(偶尔)递归调用中出现的临时值。

对于这个功能,我得到了

return bookmark
lst
tail
temp for subsets(lst[1:])

您的两个返回点是第9行(主要)和第5行(递归)。

现在开始用铅笔和纸张手动模拟初始通话。您正在做的大部分工作是维护调用堆栈存储和值。我们从主程序开始:

list(subsets([1,5,3]))

每次调用例程时,将这四个变量“推”到堆栈上(值或保留存储;只需将其设置为一个不错的框)。每次你回来时,都会“弹出”它们(使用你的橡皮擦)。

现在拨打电话。堆栈现在看起来像这样:

return: line 9
lst:    [1, 5, 3]
tail:   None
temp:   None
--------------------
list(subsets([1,5,3]))

这使您处于功能的顶部。逐行完成工作。 len(lst)不为0,所以跳转到 else 。您立即点击表达式子集(lst [1:]),这将为当前堆栈块上的 tail 提供一系列值 - 当我们返回时。现在,将下一个调用推送到堆栈:

return: line 9
lst:    [5, 3]
tail:   None
temp:   None
--------------------
return: line 9
lst:    [1, 5, 3]
tail:   None
temp:   None
--------------------
list(subsets([1,5,3]))

同样,len(lst)不是0,我们点击 else 。将该调用推入堆栈。事实上,当我们接触它时,让我们先跳过一个电话,然后推出一个电话:

return: line 9
lst:    []
tail:   None
temp:   None
--------------------
return: line 9
lst:    [3]
tail:   None
temp:   None
--------------------
return: line 9
lst:    [5, 3]
tail:   None
temp:   None
--------------------
return: line 9
lst:    [1, 5, 3]
tail:   None
temp:   None
--------------------
list(subsets([1,5,3]))

注意:在下面的处理中,我采取了一个捷径,对发生器进行了一定程度的处理,就像它返回一个列表而不是产生单独的结果一样。这是为了让答案足够简短以便理解。实际上,运行时系统一直在水平上下跳动,每次调用时只处理一个返回的叶节点列表。

现在 len(lst)== 0,所以我们 yield [] 并返回调用程序。得出的结果成为我组成的 temp 变量的值。

return: line 9
lst:    [3]
tail:   None
temp:   []
--------------------
return: line 9
lst:    [5, 3]
tail:   None
temp:   None
--------------------
return: line 9
lst:    [1, 5, 3]
tail:   None
temp:   None
--------------------
list(subsets([1,5,3]))

现在我们遍历该列表, tail 接受每个值......但是没有任何值,所以我们依次产生两个列表:[]和[3]到前一个调用。为方便起见,我将其列为 temp 中的列表。

return: line 9
lst:    [5, 3]
tail:   None
temp:   [[], [3]]
--------------------
return: line 9
lst:    [1, 5, 3]
tail:   None
temp:   None
--------------------
list(subsets([1,5,3]))

当前通话的次数为[5,3]。 tail 将采用两个值:[]和[3]。对于每一个,我们将产生该列表,无论是否加上元素 lst [0],这是** 5

[]
[5]
[3]
[5, 3]

在每一个中,我们返回上一个调用,堆栈状态为

return: line 9
lst:    [1, 5, 3]
tail:   None
temp:   [[], [5], [3], [5, 3]]
--------------------
list(subsets([1,5,3]))

最后,遍历级别将产生预期的输出:

[[], [1], [5], [1, 5], [3], [1, 3], [5, 3], [1, 5, 3]]