注意:

有些人觉得这一部分很有用,而有些人觉得它很难理解。如果你觉得它令人困惑,可以跳过它并继续下一部分。

为了展示更高级的函数使用方法,我们将通过以下程序进行讲解:

def mult(a, b):
    if b == 0:
        return 0
    rest = mult(a, b - 1)
    value = a + rest
    return value

result = mult(3, 2)
print("3 * 2 = ", result)

基本上,这个程序创建了一个正整数乘法函数(其运行速度比内建的乘法函数要慢得多),然后用一个函数调用来演示这个功能。这个程序展示了递归的使用,递归是一种迭代(重复)形式,其中函数会反复调用自身,直到满足退出条件。它使用重复的加法来给出与乘法相同的结果:例如,3 + 3(加法)与 3 * 2(乘法)得到相同的结果。

问题:程序的第一步做了什么?

回答: 第一件事是定义了函数 mult,代码如下:

def mult(a, b):
    if b == 0:
        return 0
    rest = mult(a, b - 1)
    value = a + rest
    return value

这段代码创建了一个函数,它接受两个参数并返回一个值,等到它完成计算后。之后,这个函数可以被调用。

接下来发生了什么?

接下来运行的代码是:result = mult(3, 2)

这行代码做了什么?

这行代码会将 mult(3, 2) 的返回值赋给变量 result

那么 mult(3, 2) 返回什么?

我们需要一步一步地运行 mult 函数来找出答案。

下一步是什么?

mult(3, 2) 被调用时,变量 a 被赋值为 3,变量 b 被赋值为 2。

然后呢?

接下来运行的是 if b == 0:。由于 b 的值是 2,条件为假,所以 return 0 被跳过。

那接下来呢?

运行的是 rest = mult(a, b - 1)。这行代码将 mult(a, b - 1) 的结果赋给局部变量 rest。此时,a 的值是 3,b 的值是 2,所以函数调用变成了 mult(3, 1)

那么 mult(3, 1) 返回什么?

我们需要再次运行 mult 函数,使用参数 3 和 1。

接下来发生了什么?

新的函数调用中,局部变量 a 的值是 3,b 的值是 1。由于这些是局部变量,它们不会影响到之前 mult(3, 2) 中的 ab 的值。

然后呢?

由于 b 的值是 1,if b == 0: 仍然为假,接下来的代码是 rest = mult(a, b - 1)

这行代码做了什么?

这行代码将 mult(3, 0) 的结果赋给 rest

那么 mult(3, 0) 返回什么?

我们需要再次运行 mult 函数,使用参数 3 和 0。

接下来发生了什么?

函数中第一行代码是 if b == 0:,此时 b 的值为 0,条件成立,因此执行的是 return 0

那么 return 0 做了什么?

return 0 将 0 从函数中返回。

那么?

现在我们知道 mult(3, 0) 的结果是 0。接下来,我们知道 rest = mult(a, b - 1) 执行的过程,因为我们已经运行了 mult(3, 0)。我们已经完成了 mult(3, 0) 的计算,现在回到执行 mult(3, 1),此时 rest 被赋值为 0。

接下来执行的是什么代码?

接下来执行的是 value = a + rest。在这次函数调用中,a = 3rest = 0,所以 value = 3

接下来发生了什么?

执行 return value,这将 3 从函数中返回。此时退出了 mult(3, 1) 的函数调用,并返回到 mult(3, 2)

我们在 mult(3, 2) 中停留在哪一步?

我们之前的计算中,a = 3b = 2,并且正在检查 rest = mult(a, b - 1)

那么接下来发生了什么?

此时,rest 被赋值为 3。接下来的代码是 value = a + rest,这将 value 设置为 3 + 3,即 6。

接下来发生了什么?

返回值 6 并退出了 mult(3, 2)。现在我们回到 result = mult(3, 2),它将值 6 赋给 result 变量。

接下来执行的是哪行代码?

接下来的代码是 print("3 * 2 = ", result)

这行代码做了什么?

它会打印出 3 * 2 =result 的值,即 6。完整输出是 3 * 2 = 6

总结:

基本上,我们使用了两个事实来计算两个数字的乘积。第一个事实是任何数字乘以 0 都是 0(x * 0 = 0)。第二个事实是一个数字乘以另一个数字等于第一个数字加上第一个数字乘以第二个数字减 1(x * y = x + x * (y - 1))。所以,3 * 2 被转换为 3 + 3 * 1。接下来,3 * 1 被转换为 3 + 3 * 0。我们知道任何数字乘以 0 都是 0,所以 3 * 0 = 0。然后可以计算 3 + 3 * 0 等于 3 + 0,结果是 3。现在我们知道 3 * 1 的结果是 3,因此可以计算 3 + 3 * 1 等于 3 + 3,结果是 6。

整个过程如下:

mult(3, 2)
3 + mult(3, 1)
3 + 3 + mult(3, 0)
3 + 3 + 0
3 + 3
6

递归

通过递归解决问题的编程构造称为递归。在本章中的示例中,递归是通过定义一个函数调用自身来实现的。这使得编程任务的解决变得更加简洁,因为有时可以仅考虑问题的下一步,而不是一次性解决整个问题。递归的优点是它能够以直观、易于理解的代码表达一些数学概念。

任何可以通过递归解决的问题,都可以用循环来重新实现。使用循环通常能带来更好的性能。然而,使用循环的实现通常更难正确完成。

递归的最直观定义是:

递归

如果你仍然没有理解,可以参考递归示例。如果乘法的例子不明白,试着通过阶乘的例子来理解。

示例

factorial.py

# 定义一个计算阶乘的函数

def factorial(n):
    if n == 0:
        return 1
    if n < 0:
        return "错误,负数没有阶乘值!!"
    return n * factorial(n - 1)

print("2! =", factorial(2))
print("3! =", factorial(3))
print("4! =", factorial(4))
print("5! =", factorial(5))
print("-3! =", factorial(-3))

输出:

2! = 2
3! = 6
4! = 24
5! = 120
-3! = 错误,负数没有阶乘值!!

countdown.py

def count_down(n):
    print(n)
    if n > 0:
        return count_down(n - 1)

count_down(5)

输出:

5
4
3
2
1
0
最后修改: 2025年01月11日 星期六 11:27