Python递归示例说明

时间:2017-03-30 15:53:59

标签: python function recursion

所以目前正在麻省理工学院的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]

任何帮助将不胜感激!

3 个答案:

答案 0 :(得分:2)

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

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

现在递归案例怎么样?这意味着 是一个列表元素。例如[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从列表中收集项目。在特殊情况下,当列表中的项目是列表时,该函数会调用自身来从此列表中收集项目。通过这种方式,无论人们需要挖掘多深,每一个元素都会被收集到层次结构中。这就是递归的美妙之处 - 一个自称为“魔法”的功能。访问所有的树枝和他们的叶子在树上:):