所以目前正在麻省理工学院的OpenCourseWare计算机科学课程在线工作,我在试图理解其中一个递归的例子时遇到了麻烦。
def f(L):
result = []
for e in L:
if type(e) != list:
result.append(e)
else:
return f(e)
return result
当给出以下输入时:
print f([1, [[2, 'a'], ['a','b']], (3, 4)])
输出结果为:
[2, 'a']
我无法理解这个功能实际上是如何运作的,或者它正在做什么。该函数最终不应该将每个字符串或int添加到结果列表中吗?我只是需要帮助来试图理解这个功能如何“结束”和“解开”
我觉得输出应该是:
[1,2,'a','a','b',3,4]
任何帮助将不胜感激!
答案 0 :(得分:2)
函数f
返回它遇到的第一个平面列表的(浅)副本,深度优先搜索。
为什么呢?首先让我们看一下基本案例:一个包含没有列表的列表。像[1,'a',2,5]
一样。在这种情况下,if
语句将始终成功,因此e
的所有元素都将添加到result
,result
为返回。
现在递归案例怎么样?这意味着 是一个列表元素。例如[1,['a',2],5]
。现在,对于第一个元素,if
成功,因此1
被添加到result
列表中。但对于第二个元素['a',2]
,if
失败。这意味着我们使用f
对['a',2]
执行递归调用。现在由于该列表不包含任何子列表,我们知道它将返回该列表的副本。
但请注意,我们立即return
该递归调用的结果。因此,从我们采用else
分支开始,result
已经不重要:我们将返回f(e)
返回的内容。
如果我们做出假设,我们就无法构造一个无限深的子列表循环(实际上我们可以,但在这种情况下,我们将获得堆栈溢出异常),我们最终将获得一个平面列表并获得该副本。
示例:如果我们采取您的示例输入[1, [[2, 'a'], ['a','b']], (3, 4)]
。我们可以跟踪电话。所以我们首先在该列表上调用f
,它将生成以下内容" trace":
# **trace** of an example function call
f([1, [[2, 'a'], ['a','b']], (3, 4)]):
result = []
# for 1 in L:
# if type(1) == list: # fails
# else
result.append(1) # result is now [1]
# for [[2,'a'],['a','b']] in L:
# if type([[2,'a'],['a','b']]) == list: succeeds
return f([[2,'a'],['a','b']])
result = []
# for [2,'a'] in L:
# if type([2,'a']) == list: succeeds
return f([2,'a'])
result = []
# for 2 in L:
# if type(2) == list: fails
# else:
result.append(2) # result is now [2]
# for 'a' in [2,'a']:
# if type('a') == list: fails
# else:
result.append('a') # result is now [2,'a']
return [2,'a']
return [2,'a']
return [2,'a']
<强>压扁强>:
如果您希望展平列表而不是返回第一个平面列表,则可以将代码重写为:
def f(L):
result = []
for e in L:
if type(e) != list:
result.append(e)
else:
result += f(e)
return result
请注意,仅展平list
s (甚至不包括list
的子类。)
答案 1 :(得分:1)
所以根据你建议的答案,我看到你了解代码的概念。它越陷越深,直到找到一个元素。但是看看上层的后退步骤:
当它第一次到达最深点时([2,'a']列表的元素),它在这个级别上完成循环并返回结果2和a。这是一个RETURN语句...这意味着循环是停止的,因此没有找到其他元素。
现在的问题是,为什么它没有显示1作为结果的一部分?出于同样的原因,RETURN是较低级别(2,a)和较高级别结果的结果。如果将“result”更改为全局变量,则结果将为[1,2,'a']
最好的问候
答案 2 :(得分:0)
当运行到第一个自下而上的list-element并且不包含列表时,作为发布的函数返回/退出 - 这可以防止遍历递归的所有其他分支。例如:
print( f([1, [[2, 'a', [3, 'b', [4, 'c']]], ['a','b']], (3, 4)]) )
# gives: [4, 'c']
print( f([1, ['X','Y'], [[2, 'a', [3, 'b', [4, 'c']]], ['a','b']], (3, 4)]) )
# gives: ['X','Y']
导致此行为的关键点是
行result = []
这会将列表中的每次调用结果重置为空列表。这样,只有一个项目从递归调用链中返回。
顺便说一句,下面的函数f能达到预期的效果,不是吗?
def f(L, result):
for e in L:
if type(e) != list:
result.append(e)
else:
f(e, result)
result=[]; f([1, [[2, 'a', [3, 'b', [4, 'c']]], ['a','b']], (3, 4)], result) print( result )
# gives: [1, 2, 'a', 3, 'b', 4, 'c', 'a', 'b', (3, 4)]
result=[]; f( [1, ['X','Y'], [[2, 'a', [3, 'b', [4, 'c']]], ['a','b']], (3, 4)], result); print( result )
# gives: [1, 'X', 'Y', 2, 'a', 3, 'b', 4, 'c', 'a', 'b', (3, 4)]
注意:(3,4)是TUPLE而不是列表...
如果这些项目本身不是列表,则上述函数f从列表中收集项目。在特殊情况下,当列表中的项目是列表时,该函数会调用自身来从此列表中收集项目。通过这种方式,无论人们需要挖掘多深,每一个元素都会被收集到层次结构中。这就是递归的美妙之处 - 一个自称为“魔法”的功能。访问所有的树枝和他们的叶子在树上:):