了解递归流程

时间:2018-11-06 22:08:54

标签: python python-3.x

发现缠住递归的过程很棘手,将不胜感激。 我试图通过依次将数字除以2来理解递归流程。

def divide_by_2(num):

    if num > 1:
       divide_by_2(num // 2)
       print(num)
    print(num % 2,num)

divide_by_2(39)

输出:

  

1 1

     

2

     

0 2

     

4

     

0 4

     

9

     

1 9

     

19

     

1 19

     

39

     

1 39

我的问题是

1)不应该首先打印1 39,为什么要在最后打印它?为什么顺序被颠倒了?

2)应该先执行两个print语句中的哪个,为什么?

4 个答案:

答案 0 :(得分:2)

动手并遵循代码行(从底线开始,因为这是一切的开始),每次遇到divid_by_2时,都跳回顶部

您会注意到,从39开始进入函数,然后在打印任何内容之前又打了一个对divid_by_2的调用。

您将一直这样,直到下降到1,这时您将跳过对divid_by_2的调用,然后在调用堆栈展开时以相反的顺序开始打印内容

之所以能够解决问题,是因为当您退出函数调用时,代码将从函数调用后的下一行继续

下一行是打印语句,因此要打印的第一件事是最小的除法结果

使用一种具有交互式调试器功能的语言可能会极大地帮助您理解,因此您可以单步执行代码并查看其是否正常运行,但是,如果您没有,可以将其视为多级代码缩进:

divide_by_2(64)

print(64)



divide_by_2(64)
  divide_by_2(32)

  print(32)
print(64)

每次递归时,都将一个分隔/打印对复制粘贴到中间的空白行中,缩进量增加以表明调用堆栈越来越长

divide_by_2(64)
  divide_by_2(32)
    divide_by_2(16)

    print(16)
  print(32)
print(64)


divide_by_2(64)
  divide_by_2(32)
    divide_by_2(16)
      divide_by_2(8)
        divide_by_2(4)
          divide_by_2(2)
            divide_by_2(1)
            print(1)
          print(2)
        print(4)
      print(8)
    print(16)
  print(32)
print(64)

您问先打印哪个打印件,如果您谈论代码中包含两个打印语句(在源代码中最先出现的打印语句)这一事实,则比较简单。

如果您要说为什么最后打印39条,那是因为您的代码在停止递归之前一直沿其尽可能深的位置进行遍历,并且打印在退出过程中完成。如果在递归到除法函数之前先进行打印,则数字将从39开始并变小

答案 1 :(得分:1)

以较小的数字开头,因此您可以一次映射所有递归。我还将略微重新编写您的函数,以简化可重复性

def divide_by_2(num):
    if num == 0:
        return  # we're done
    divide_by_2(num//2)
    print(num)

现在我们除以5 ...

divide_by_2(5)
-> divide_by_2(2); print(5)
-> divide_by_2(1); print(2); print(5)
-> divide_by_2(0); print(1); print(2); print(5)
-> return; print(1); print(2); print(5)

基本上:由于您已将递归调用放在该函数的其余部分之前,因此它必须先完成其递归,然后才能继续。

答案 2 :(得分:1)

由于39为> 1,所以它将进入初始if语句。该块中的第一行代码告诉它使用divide_by_2(num // 2)递归调用自身(取整数Floor)。没有打印语句命中-仅将另一个divide_by_2放在调用堆栈divide_by_2(19)上。因此,这将继续添加到调用堆栈中,并调用 no 打印语句,直到num为<1。

在调用堆栈的顶部,您会得到1 1,因为该调用不是进入条件if的,而是立即进入print(num % 2,num)

此后,它返回到调用堆栈,在该堆栈中,每个函数可以同时执行(并完成)print(num)print(num % 2, num),直到在堆栈上没有其他函数可以执行为止。 / p>

希望可以稍微解释一下其背后的“理论”。

答案 3 :(得分:1)

如果我们采用您的代码(并添加行号以方便说明)

1 def divide_by_2(num):
2    if num > 1:
3       divide_by_2(num // 2)
4       print(num)
5    print(num % 2,num)

然后以较小的一行一行为例

divide_by_2(4)
  

(调用1,num == 4)执行了第2行,它大于1,因此移至   第3行

     

(调用1,num == 4)第3行调用相同的函数。它不会继续   到第4行,直到函数调用2完成执行并返回。   我们尚未调用打印声明,因此没有任何显示!

     

(调用2,num == 2)执行了第2行,它大于1,因此移至   第3行

     

(调用2,num == 2)第3行再次通过同一函数递归   在调用3完成之前,不要继续执行。仍然没有打印   声明

     

(调用3,num == 1)执行第2行,它等于1,移至第5行

     

(调用3,num == 1)第5行我们终于有了一条打印语句!我们在   最内层的num == 1调用,因此将打印“ 1 1”。现在,这个   函数已完成,我们在最近一次调用中返回到第4行   在这种情况下称为呼叫2

     

(调用2,num == 2)第4行随着函数的返回,我们开始   执行下一行,即print(num),仅打印“ 2”

     

(呼叫2,num == 2)第5行我们还有另一个打印语句,这是   打印“ 0 2”。调用2完成执行后,调用1继续执行

     

(呼叫1,num == 4)第4行显示“ 4”

     

(呼叫1,num == 4)第5行显示“ 0 4”

     

执行结束

     

这给出了以下结果:

     

1 1

     

2

     

0 2

     

4

     

0 4

要记住为什么从小到大打印数字的关键是,程序中的流程会在 递归到下一个函数后打印语句。这导致行为调用1->调用2->调用3->从调用3打印->从调用2打印->从调用1打印。

对我而言,最简单的递归方法是在纸上或脑海中逐行浏览代码。我希望这种故障可以有所帮助。