递归中的返回语句

use*_*661 1 python recursion

所以我对递归的概念有了相当不错的理解,但是有些实现真的让我感到震惊.以这个简单的斐波那契函数为例:

def fib(x):
    if x == 0 or x == 1:
        return 1
    else:
        return fib(x-1) + fib(x-2)
Run Code Online (Sandbox Code Playgroud)

我知道这会将斐波纳契计算分解成更小的更易处理的块.但它究竟是如何达到最终结果的呢?在递归情况下返回的确切内容是什么?看起来它只是返回一个函数调用,它将继续调用函数直到它返回1 - 但它似乎永远不会做任何真正的计算/操作.将其与经典的阶乘函数进行对比:

def factorial(n):
    if n == 1:
         return 1
    else:
         return n * factorial(n) 
Run Code Online (Sandbox Code Playgroud)

这里,函数显然在n上运行,每次定义一个整数,而fibonacci函数只对函数本身进行操作,直到返回1.

最后,当我们带来类似Merge Sort算法的东西时,事情变得更加怪异; 即这一段代码:

    middle = int(len(L)/2)
    left = sort(L[:middle], lt)
    right = sort(L[middle:], lt)
    print(left, right)
    return merge(left, right, lt)
Run Code Online (Sandbox Code Playgroud)

left和right似乎是递归调用sort,但print语句似乎表明merge正在处理每个递归调用.那么每个递归调用以某种方式"保存"然后在返回时最终调用合并时进行操作?我越来越混淆自己的第二个......我觉得我已经接近了对递归的强烈理解,但是我对递归调用的确切回复的理解正在阻碍我.

dyo*_*yoo 9

试试这个练习:

fib(0)的价值是多少?fib(1)的价值是多少?让我们把它们写下来.

fib(0) == 1
fib(1) == 1
Run Code Online (Sandbox Code Playgroud)

我们知道这是因为这些是"基本情况":它匹配fib定义中的第一种情况.

好吧,让我们提一下吧.fib(2)的价值是多少?我们可以看看函数的定义,它将是:

fib(2) == fib(1) + fib(0)
Run Code Online (Sandbox Code Playgroud)

我们知道fib(1)fib(0)的价值是什么:这些都会做一些工作,然后给我们一个答案.所以我们知道fib(2)最终会给我们一个价值.

好的,搞砸了.fib(3)的价值是多少?我们可以看看定义,它将是:

fib(3) == fib(2) + fib(1)
Run Code Online (Sandbox Code Playgroud)

我们已经知道fib(2)fib(1)最终将为我们计算数字. fib(2)将比fib(1)做更多的工作,但它们最终都会触底,给我们添加的数字.

首先考虑小案例,看看当你提出问题的大小时,子问题就是我们知道如何处理的事情.

如果你已经完成了标准的高中数学课程,你将会看到类似的东西:数学家使用所谓的"数学归纳",这与我们程序员用作工具的递归是一样的.


Ben*_*Ben 6

不理解递归函数如何工作是很常见的,但它确实表明你只是不理解函数和返回是如何工作的,因为递归函数与普通函数完全相同.

print 4
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为该print语句知道如何打印值.它被赋予值4,并打印出来.

print 3 + 1
Run Code Online (Sandbox Code Playgroud)

print声明不了解如何打印3 + 1.3 + 1这不是一个价值,而是一种表达.幸运的是print,不需要知道如何打印表达式,因为它永远不会看到它.Python将传递给事物,而不是表达式.所以Python所做的是在执行代码时评估表达式.在这种情况下,这会导致4产生价值.然后将值4赋给print语句,该语句可以快乐地打印出来.

def f(x):
    return x + 1

print f(3)
Run Code Online (Sandbox Code Playgroud)

这与上面非常相似.f(3)是表达式,而不是值.print用它做不了什么.Python必须评估表达式以产生一个值来打印.它通过查找名称来实现f,幸运的是找到了由def语句创建的函数对象,并使用参数调用该函数3.

这导致函数的主体被执行,并且被x绑定3.与在这种情况下一样print,return语句不能对表达式执行任何操作x + 1,因此Python会对该表达式求值以尝试查找值.x + 1with xbound 3生成值4,然后返回.

从函数返回值使得函数调用表达式的求值变为该值.因此,回过头来print f(3),Python已经成功地将表达式计算f(3)为值4.哪个print可以打印.

def f(x):
    return x + 2

def g(y):
    return f(y * 2)

print g(1)
Run Code Online (Sandbox Code Playgroud)

在这里,g(2)表达式不是值,因此需要进行评估.评估g(2)将我们带到f(y * 2)y必然1.y * 2不是一个价值,所以我们不能呼吁f它; 我们必须首先评估它,这会产生价值2.然后,我们可以调用f2,它返回x + 2x绑定到2.x + 2计算值4,该值从中返回f并成为表达式f(y * 2)内部的值g.这最终给出了g返回值,因此表达式g(1)被计算为值4,然后打印出来.

请注意,在向下钻取以评估f(2)Python仍然"记住"它已经处于评估的中间时g(1),一旦知道f(2)评估结果,它就会回到正确的位置.

而已.这就是全部.您不需要了解有关递归函数的任何特殊内容.return使调用此函数的特定调用的表达式成为赋予的值return.在直接表达,而不是一些更高级别的表达式称为是称为调用函数函数功能.最里面的一个.这不要紧,中间函数调用是否碰巧是相同的功能,这一个或没有.return甚至无法知道是否以递归方式调用此函数,更不用说在这两种情况下表现不同了.return 总是总是将它的值返回给这个函数的直接调用者,无论它是什么.它永远不会永远 "跳过"任何这些步骤,并将值更多地返回给调用者(例如递归函数的最外层调用者).

但是为了帮助您了解这一点,我们可以fib(3)更详细地跟踪评估.

fib(3):
    3 is not equal to 0 or equal to 1
    need to evaluate fib(3 - 1) + fib(3 - 2)
        3 - 1 is 2
        fib(2):
            2 is not equal to 0 or equal to 1
            need to evaluate fib(2 - 1) + fib(2 - 2)
                2 - 1 is 1
                fib(1):
                    1 is equal to 0 or equal to 1
                    return 1
                fib(1) is 1
                2 - 2  is 0
                fib(0):
                    0 is equal to 0 or equal to 1
                    return 1
                fib(0) is 1
            so fib(2 - 1) + fib(2 - 2) is 1 + 1
        fib(2) is 2
        3 - 2 is 1
        fib(1):
            1 is equal to 0 or equal to 1
            return 1
        fib(1) is 1
    so fib(3 - 1) + fib(3 - 2) is 2 + 1
fib(3) is 3
Run Code Online (Sandbox Code Playgroud)

更简洁,fib(3)回报fib(2) + fib(1).fib(1)返回1,但fib(3)返回加上结果fib(2).fib(2)回报fib(1) + fib(0); 两者都返回1,所以将它们加在一起会fib(2)得到结果2.回到fib(3)原来fib(2) + fib(1),我们现在可以说那是2 + 1什么了3.

你失踪关键的一点是,虽然fib(0)fib(1)回报1,那些1S型更高级别的呼叫总计达表达式的一部分.