Python递归示例说明

Python递归示例说明,python,function,recursion,Python,Function,Recursion,因此,目前正在通过麻省理工学院的开放式计算机科学课程在线学习,我很难理解其中一个递归示例 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)])

因此,目前正在通过麻省理工学院的开放式计算机科学课程在线学习,我很难理解其中一个递归示例

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]

任何帮助都将不胜感激,谢谢

函数
f
返回深度优先搜索遇到的第一个平面列表的(浅)副本

为什么??首先让我们看一看基本情况:一个包含no列表的列表。像
[1,'a',2,5]
。在这种情况下,
if
语句将始终成功,因此
e
的所有元素将添加到
结果中,并返回
结果

那么递归的情况呢。这意味着有一个列表元素。例如
[1,[a',2],5]
。现在对于第一个元素,
if
成功,因此
1
被添加到
结果
列表中。但是对于第二个元素
['a',2]
,如果
失败,则
。这意味着我们使用
['a',2]
f
执行递归调用。现在,由于该列表不包含任何子列表,我们知道它将返回该列表的副本

但是请注意,我们立即
返回该递归调用的结果。因此,从我们使用
else
分支的那一刻起,
结果就不再重要了:我们将返回
f(e)
返回的内容

如果我们假设我们不能构造无限深子列表的循环(实际上我们可以,但在这种情况下我们将得到堆栈溢出异常),我们最终将获得一个平面列表并获得该副本

示例:如果我们采用您的示例输入
[1,2,a'],[a'],[b'],(3,4)]
。我们可以追踪电话。因此,我们首先在该列表上调用
f
,它将生成以下“跟踪”:

示例函数调用的跟踪** f([1,2,a'],[a'],[b'],(3,4)]: 结果=[] #对于L中的1: #如果类型(1)=列表:#失败 #否则 result.append(1)#结果现在是[1] #对于L中的[[2,'a'],['a','b']]: #如果类型([[2,'a'],['a','b']])==列表:成功 返回f([[2,'a'],['a'],'b']) 结果=[] #对于L中的[2,'a']: #如果类型([2,'a'])==列表:成功 返回f([2,'a'])) 结果=[] #对于L中的2: #如果类型(2)==列表:失败 #其他: result.append(2)#结果现在是[2] #对于[2,'a']中的'a': #如果类型('a')==列表:失败 #其他: result.append('a')#结果现在是[2,'a'] 返回[2,'a'] 返回[2,'a'] 返回[2,'a']
展平

如果您希望展平列表而不是返回第一个平面列表,则可以将代码重写为:

def f(L):
    result = []
    for e in L:
        if type(e) != list:
            result.append(e)
        else:
            result += f(e)
    return result
def(L):
结果=[]
对于L中的e:
如果类型(e)!=名单:
结果.追加(e)
其他:
结果+=f(e)
返回结果

请注意,这将仅展平
列表
s
(甚至不会展平
列表
s的子类)。

因此,根据您建议的答案,我认为您理解了代码的概念。它越挖越深,直到找到一个元素。但看看返回到上层的那一步:

当它第一次到达最深的点(即[2,'a']列表的元素)时,它完成这个级别的循环并返回结果2和a。这是一个返回语句。。。这意味着循环停止,因此找不到其他元素

现在悬而未决的问题是,为什么它不显示1作为结果的一部分?出于同样的原因,返回是较低级别(2,a)和较高级别的结果。如果将“result”更改为全局变量,结果将是[1,2,'a']


当运行到第一个不包含列表的自下而上的列表元素时,最好将函数as posted返回/退出-这会阻止遍历递归的所有后续分支。例如:

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)元组不是列表


如果列表中的项目本身不是列表,则上述函数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)]