我现在正在通过阅读 Thinking Python 来学习Python的基础知识。
当我阅读有关“富有成果的”功能(即“返回”值的功能)的一章时,我遇到了这个问题。我或多或少可以弄清楚阶乘函数的代码是如何工作的,但不完全是。我很难准确地理解涉及自调用或递归的函数的流程。顺便说一句,下面所示代码中的打印机语句是脚手架,一种用于跟踪功能流程的设备。
事不宜迟,这里是代码。
def factorial(n):
space=' '*(2*n)
print(space, 'factorial', n)
if n==0:
print(space, 'returning 1')
return 1
else:
recurse = factorial(n-1)
result=n*recurse
print(space, 'Returning', result)
return result
factorial(3)
此代码的结果或输出令人震惊地是v形。我一生都无法弄清为什么计算机(或编译器?)以此处的方式运行程序。看来它先打印第一行和最后一行,然后继续打印第二行和第二到最后一行...这很奇怪。您会期望它总是自上而下。
为了说明这个问题的怪异之处,我在此附加另一段代码,在您递减计数时打印数字。该代码是相似且更简单的,并且您希望它的行为与之前的阶乘函数相似。但事实并非如此。结果行显示或打印的顺序不奇怪。
def f(n):
print(n)
if n==0:
return 0
else:
print(3*n)
f(n-1)
return
如果您能帮助我,我将不胜感激!
答案 0 :(得分:2)
可能使您感到困惑的是,与示例Control-C
一样,递归调用不会在函数的末尾发生。这意味着,您有在进入递归之前执行的代码,而在 从递归中退出之后执行的代码。让我们看一下f()
中会发生什么:
递归步骤
选择您喜欢的任何递归步骤factorial
,这不是基本情况。然后查看函数的功能,而无需进行递归操作。
对于您的i
函数,让我们来调用factorial
,其中factorial(i)
与i
不同。该调用完成了三件事:
0
空格和“阶乘i” 2*i
空格并“返回i” 因此,输出将是:
2*i
您将看到(2*i spaces) factorial i
# everything factorial(i-1) outputs
(2*i spaces) returning i
的输出夹在factorial(i-1)
的输出之间。要执行递归,您需要做的就是炸毁图案。让我们再考虑一步factorial(i)
:
i-1
因此,您看到缩进随着调用而减少,因为(2*i spaces) factorial i
(2*(i-1) spaces) factorial i-1
# everything factorial(i-1) outputs
(2*(i-1) spaces) returning i-1
(2*i spaces) returning i
减少,这导致 v形。
基本情况
它只是打印
i
将它们放在一起
现在,您可以找到factorial 0
returning 1
所做的口头表达:
factorial(i)
产生V形输出
factorial(i)
在 factorial i
factorial i-1
...
factorial 0
returning 1
...
returning i-1
returning i
情况为真的情况下,请使用i == 0对i+1
是正确的,则从那里得出结论。 >递归步骤。完成后,您可以确定i
对任何factorial(n)
的工作方式。
答案 1 :(得分:1)
这是对您的代码进行了一些重组以使其更易于查看:
def factorial(n):
space = ' '*(2*n)
print(space, 'factorial', n)
result = 1 if n == 0 else n*factorial(n-1)
print(space, 'Returning', result)
return result
这是factorial(3)
的输出,其中包含您提到的“ V形”:
factorial 3
factorial 2
factorial 1
factorial 0
Returning 1
Returning 1
Returning 2
Returning 6
它看起来正确,可以完成阶乘的计算。要了解此处看到的打印顺序,请考虑以下几点:
3
开始的,因为这是您的切入点3x2 = 6
个空格factorial(3)
,因此需要重新使用factioral(2)
,因此接下来调用此方法,缩进2x2 = 4
个空格。对于所有其他阶乘,依此类推。这就是上半部分的V形的来源。factorial(0)
可以传递该返回值而无需再次递归,因此它将打印第一个返回值。缩进为0
。factorial(1)
的函数调用现在可以通过将返回的1
乘以自己的n
来计算其结果,得出1
。这是下一个打印,缩进为2
。最重要的是要理解,函数(以及打印)的结果值只能出现在其下的完整递归树的END。这就是为什么调用print factorial 3
在第一行,而其相应的Returning 6
print在最后一行-在整个内部树完成之后的原因。
很难说我是否掌握了您难以理解的关键点,但希望对您有所帮助。
编辑:无限递归
为回答您的问题,下面的示例显示了每个递归函数调用将如何具有自己的变量范围,因此不知道在同一函数的另一个实例中设置的变量,最终以{{1 }}达到调用堆栈的最大递归深度时。
RecursionError