1、函数执行流程
(调用函数,保存当前的内容,压栈函数并创建栈帧。执行里面的语句)
全局帧中生成foo1、foo2、foo3、main的函数对象。(栈,先进后出,后进先出)。
main函数调用
main 中查找内建函数print压栈,将常量字符串压栈,调用函数,弹出栈顶。
main中全局函数foo1压栈,将常量100,101压栈,调用函数foo1,创建栈帧。Print函数压栈,字符串和变量b、b1压栈,调用函数,弹出栈帧,返回值。
Main中全局查找foo2函数压栈,将常量200压栈,调用foo2,创建栈帧。foo3函数压栈,变量c引用压栈,调用foo3,创建栈帧。foo3完成print函数调用后返回。foo2回复调用,执行print后,返回值。Main中foo2调用结束弹出栈顶,main函数继续执行print函数调用,弹出栈顶,main函数返回。
2、递归:函数直接或者间接调用自身就是递归。
递归需要有边界条件、递归前进段、递归返回段。
递归一定要有边界条件。
当边界不满足的时候递归前进。
当边界条件满足的时候,递归返回。
斐波那契数列:
pre = 0
cur = 1
print(pre,cur,end = ' ')
n=4
for i in range(n-1):
pre,cur = cur ,pre + cur
print(cur,end=' ')
def fib(n):
return 1 if n<2 else fib(n-1)+fib(n-2)
for i in range(5):
print(fib(i),end=' ')
3、递归要求
递归一定要有退出条件,递归调用一定要执行到这个退出条件,没有退出条件的递归调用,就是无限调用。
递归调用的深度不宜过深。
Python中对递归调用的深度做了限制,以保护解释器。
超过递归调用深度,会抛出异常的。
4、递归的性能
循环稍微复杂一些,但是只要不是死循环,可以多次迭代直至算到结果。
改进。左边的fib函数和循环的思想类似。
参数n是边界条件,用n来计数。
上一次的计算结果作为下一次结果的实参。
import datetime
n=35
start = datetime.datetime.now()
def fib(n):
return 1 if n<2 else fib(n-1)+fib(n-1)
for i in range(n):
print(fib(i),end='')
delta = (datetime.datetime.now()-start).total_seconds()
print(delta)
效率比较低。
pre = 0
cur = 1
print(pre,cur,end=' ')
def fib(n,pre=0,cur=1):
pre,cur = cur,pre + cur
print(cur,end=' ')
if n == 2:
return
fib(n-1,pre,cur)
print(fib(5))
#斐波那契数列改进方式
左边的fib函数和循环的思想类似
参数n是边界条件,用n来计数。
上一次的计算结果作为函数的实参。
效率很高
和循环相比,性能相近,所以说递归效率不一定很低。但是深度有限。
5、间接递归
def foo1():
foo2()
def foo2():
f1oo()
foo1()
是通过别的函数调用了函数本身。
但是,如果构成了循环递归调用时非常危险。
6、递归总结
是一种很自然的表达,符合逻辑思维。
相对运行效率较低,每次调用函数都要开辟栈帧
递归有深度限制,如果递归层次太深,函数反复压栈,栈内存很快就溢出了。
如果有限次数的递归,可以使用递归调用,或者使用循环替代,循环代码稍微复杂,但是只要不是死循环,可以多次迭代直至算出结果。
绝大多数递归,都可以使用循环实现。
即使代码很简单,但是能不用则不用递归。