尾调用是在函数的尾部,调用另一个函数,因为函数是语言没有循环,递归很重要,对于递归需要来不断优化,一般采用尾调用或尾递归来优化。

顾名思义,尾递归就是从最后开始计算, 每递归一次就算出相应的结果, 也就是说, 函数调用出现在调用者函数的尾部 ,尾递归就是把当前的运算结果(或路径)放在参数里传给下层函数和普通递归区别在于内存占用。

 

循环与递归(阶乘的例子)

 

循环 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
 >>>