码迷,mamicode.com
首页 > 编程语言 > 详细

Python-递归函数

时间:2018-08-24 21:18:15      阅读:240      评论:0      收藏:0      [点我收藏+]

标签:jupyter   nbsp   包括   pycharm   逻辑   isp   查找   变形   代码简洁   

阅读目录:

  1、函数执行流程 

  2、递归Recursion

  3、递归练习

 

内容:

1、函数执行流程 

 http://pythontutor.com/visualize.html#mode=display # 在这个网站可以输入代码,查看演示
或者pycharm debug查看frames
 1 def foo1(b, b1=3):
 2     print(foo1 called, b, b1)
 3 def foo2(c):
 4     foo3(c)
 5     print(foo2 called, c)
 6 def foo3(d):
 7     print(foo3 called, d)
 8     
 9 def main():
10     print(main called)
11     foo1(100, 101)
12     foo2(200)
13     print(main ending)
14     
15 main()
16 -------------------------------------------------
17 main called
18 foo1 called 100 101
19 foo3 called 200
20 foo2 called 200
21 main ending

 

 对上面的执行流程做一个简单的描述:

    1. 全局帧中生成foo1,foo2 ,foo3,main函数对象
    2. main函数调用
    3. main中查找内建函数print 压栈,将常量字符串压栈,调用函数,弹出栈顶
    4. main 中全局查找函数foo1 压栈,将常量100 ,101 压栈,调用函数foo1 ,创建栈帧。print函数压栈,字符串 和 变量 b, b1 压栈,调用函数,弹出栈顶,返回值。
    5. main中全局查找foo2 函数压栈,将常量200,压栈,调用foo2,创建栈帧。foo3 函数压栈,变量c引用压栈,调用foo3,创建栈帧,foo3 完成print 函数调用后返回,foo2 恢复调用,执行print后,返回值,main 中foo2 调用结束弹出栈顶,main 继续执行print 函数调用,弹出栈顶,main返回。

 

 技术分享图片

图1,函数读取到内存中

技术分享图片

图2:执行main函数时,调用foo2(200)时候的栈帧图

 

   注:

    •  函数执行 要压栈,函数内执行函数,内层函数要落在外层函数上面。
    • 函数结束要弹出,包括函数需要的参数等。
    • 栈跟线程相关
      • 定义一个函数,两个线程分别调用该函数,各自在各自的栈空间,每个线程互不影响。
    •  所以从这里可以看到局部变量  local 函数调用时创建,调用结束消亡(调用时要压栈,结束时弹出,之后局部变量引用不到了)

  注:

 1 def fn(a):
 2     print(a)
 3 
 4 g = [1]
 5 
 6 fn(g)
 7 
 8 # 弹出消亡只是 a,并不是g,如果引用类型,只是 地址,如上面
 9 # 事实上,g=【1】是放堆里的,开辟内存空间,受到GC管理
10 # 栈只是放引用地址

 

   

2、递归Recursion

  • 函数直接或者间接调用自身就是递归
  • 递归需要有边界条件,递归前进段,递归返回段。
  • 递归一定要有 边界条件
  • 当边界条件不满足的时候,递归前进
  • 当边界条件满足的时候,递归返回。   

  2.1:斐波那契数列:1,1,2,3,5,8,13......  

技术分享图片
 1 ---------------------斐波那契数列
 2 # no1 最初始的版本 使用for循环完成,交换
 3 def fib(n):
 4     a = 0
 5     b = 1
 6     for i in range(n):
 7         a, b = b, a + b
 8     return a
 9 print(fib(3)) # 2
10 
11 # no2.1 使用递归完成,但是这个版本的 递归效率极低,通过 ipython的jupyter 的%%timeit测试, 还不如循环。因为return的fib(n-1)+fib(n-2),d都要分别往下继续执行,数据量大的时候,这个相当于重复了很多,前面算了的,后面fib(n-2)里还要算一次,所以效率极低。
12 def fib(n):
13     if n < 3:
14         return 1
15     return fib(n-1) + fib(n-2)
16 
17 print(fib(3)) # 2
18 
19 # no2.2 变形版本:
20 def fib(n):
21     return 1 if n < 3 else fib(n-1) + fib(n-2)
22 
23 print(fib(3)) # 2
24 
25 # no 3 利用上次的结果的递归
26 # 这里使用 缺省参数作为运算结果,而n 作为循环次数控制量
27 def fib(n, a=0, b=1):               def fib(n, a=0, b=1):                   def fib(n, a=0, b=1):
28     if n == 0:                          if n == 0:                               if n == 0:
29         return a                             return a                               return a
30     a, b = b, a + b                     a, b = b, a + b                           a, b = b, a + b
31     return fib(n-1, a, b)              return fib(n-1, a, b)                     return fib(n-1, a, b)
32 
33 print((fib(3)))
斐波那契数列递归以及最初版本实现

 

技术分享图片

  2.2:各个解释器对针对递归的保护机制:到达这个数据,就会抛异常,不管你是不是还要进行!

    CPython中:   

      import  sys
      print((sys.getrecursionlimit())) # 1000

    IPython中: 是3000

    注: 但是事实上是不够这个数字,因为当前栈不可能只有自己的函数,可能包含其他的数据,基础性数据等。会占一部分栈空间。

    递归要求:

      • 递归一定要有退出条件,递归调用一定要执行到这个退出条件。没有退出条件的递归调用,就是无限调用。
      • 递归调用的深度不宜过深
        • Python对递归调用的深度做了限制,以保护解释器
        • 超过递归深度限制,抛出 RecursionError:maxinum recursion depthexceeded 超出最大深度
        • sys,getrecursionlimit()

    递归性能:

      • for循环,递归两者的性能对比,事实上,for循环的性能比递归性能要强,尤其数据量大的时候。
      • 循环稍微复杂一些,但是只要不是死循环,可以多次迭代直至算出结果
      • fib 函数代码简洁易懂,但是只有获取到嘴歪层的函数调用,内部递归结果都是中结案结果,而且给定一个n都要进行2n次递归,深度 越深,效率月底,为了获取斐波那契数列需要外面在套一个N次的循环,效率就更低了,但是for循环的空间复杂度比较高。
      • 递归还有深度极限,如果递归复杂,函数反复压栈,栈内存很快就溢出了。   

    节间递归:

        def foo1():

          foo2()

        def foo2():

          foo1()

        foo1()

        间接递归,是通过别的函数调用了函数本身

        但是,如果构成了循环递归调用时非常危险的,但是往往这种情况在代码复杂的情况下,还是可能发生这种调用,要用代码的规范来避免这种 递归调用的发生。

    递归总结:

      • 递归是一种很自然的表达,符合逻辑思维
      • 递归相对运行效率低,每一次调用函数都要开辟栈帧,每次要压栈,但是效率比较低。
      • 递归有深度限制,如果递归层次太深,函数反复压栈,栈内存很快就溢出了。
      • 如果是有限次数的递归,可以使用递归调用,或者使用循环替代,循环代码稍微复杂些,但是只要不是西孙焕可以多次迭代直至算出结果。
      • 绝大多数递归,都可以使用循环实现。
      • 线上,能不用则不用。

3、递归练习

https://www.cnblogs.com/JerryZao/p/9531951.html

 

Python-递归函数

标签:jupyter   nbsp   包括   pycharm   逻辑   isp   查找   变形   代码简洁   

原文地址:https://www.cnblogs.com/JerryZao/p/9531753.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!