尾调用是在函数的尾部,调用另一个函数,因为函数是语言没有循环,递归很重要,对于递归需要来不断优化,一般采用尾调用或尾递归来优化。
顾名思义,尾递归就是从最后开始计算, 每递归一次就算出相应的结果, 也就是说, 函数调用出现在调用者函数的尾部 ,尾递归就是把当前的运算结果(或路径)放在参数里传给下层函数和普通递归区别在于内存占用。
循环与递归(阶乘的例子)
循环 cycle cyclic
打印中间过程的循环
>>> def funcA2(n):
fact=1
for i in range(n):
fact=fact*(i+1)
print(i+1,"'s fact is",fact) #这个可以不要
return fact>>> funcA2(5)
1 's fact is 1
2 's fact is 2
3 's fact is 6
4 's fact is 24
5 's fact is 120
120
>>>
简写循环
>>> def funcCycle(n):
fact=1
for i in range(1,n+1,1):
fact*=i
return fact>>> funcCycle(5)
120
递归 recursion
什么叫递归?如果一个函数在内部调用自己,就被称为递归
(1)递归理论上全部都可以写成递归?
(2)递归写起来看起来比循环要简单,逻辑更清晰
>>> def funcA1(n):
if n ==1:
return 1
return n*funcA1(n-1)>>> funcA1(5)
120
定义阶乘factorial lambda表达式;递归实现
>>> funcAA1=(lambda n:1 if n==1 else n*funcAA1(n-1))
>>> funcAA1(5)
120
递归的问题:
(1) 这种递归,是反复调用自己,直到条件满足(和while相反逻辑)
(2)但是因为反复调用,会导致在最后1次计算,计算压力大
(3)使用递归函数需要注意防止栈溢出。
函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
(4)不能用在生成器函数和协程中??
>>> import sys
>>> sys.getrecursionlimit()
1000
可以试试fact(1000)
:会报错 maximum recursion depth exceeded
stack的堆积
5*4*3*2*funcA1(1)
5*4*3*funcA1(2)
5*4*funcA1(3)
5×funcA1(4)
尾递归 tail_recursion
尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。
(1)解决递归调用栈溢出的方法是通过尾递归优化
事实上尾递归和循环的效果是一样的,把循环看成是一种特殊的尾递归函数也是可以的。
(2) 每次递归都计算了一次,没放到最后计算,压力也小
(3)尾递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。
>>> def funcRecursion1(n,product=1):
if n==1:
return product
return funcRecursion1(n-1,n*product)>>> funcRecursion1(5,1)
120
>>> funcRecursion1(3,1)
6
>>>