我正在学习考试并遇到了这个问题。
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])))

我们应该在不使用计算机的情况下写下输出内容。
现在这是一个迭代器,在函数之前放置列表会将程序产生的任何内容放入列表中。
我进入第三次递归并且我很难跟踪将要发生的事情,因为我们正在对递归的结果运行循环。
是否有任何技巧/方法来绘制某种树/表格以帮助我更好地理解这段代码的输出?
答案 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]]