使用递归的阶乘函数流

时间:2019-01-11 05:52:06

标签: python-3.x function recursion

我现在正在通过阅读 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

如果您能帮助我,我将不胜感激!

2 个答案:

答案 0 :(得分:2)

可能使您感到困惑的是,与示例Control-C一样,递归调用不会在函数的末尾发生。这意味着,您有在进入递归之前执行的代码,而在 从递归中退出之后执行的代码。让我们看一下f()中会发生什么:

递归步骤
选择您喜欢的任何递归步骤factorial,这不是基本情况。然后查看函数的功能,而无需进行递归操作。

对于您的i函数,让我们来调用factorial,其中factorial(i)i不同。该调用完成了三件事:

  1. 打印0空格和“阶乘i”
  2. 致电阶乘(i-1)
  3. 打印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
  • 从内部到外部,对于所有更高级别的递归步骤,依此类推。这就是V形的后半部分的形成方式。

最重要的是要理解,函数(以及打印)的结果值只能出现在其下的完整递归树的END。这就是为什么调用print factorial 3在第一行,而其相应的Returning 6 print在最后一行-在整个内部树完成之后的原因。

很难说我是否掌握了您难以理解的关键点,但希望对您有所帮助。

编辑:无限递归

为回答您的问题,下面的示例显示了每个递归函数调用将如何具有自己的变量范围,因此不知道在同一函数的另一个实例中设置的变量,最终以{{1 }}达到调用堆栈的最大递归深度时。

RecursionError