函数执行流程、递归函数、匿名函数

函数执行流程

示范代码

def foo1(b,b1=3):
    print("foo1 called",b,b1)
def foo2(c):
    foo3(c)
    print("foo2 called",c)
def foo3(d):
    print("foo3 called",d)
def main():
    print("main called")
    foo1(100,101)
    foo2(200)
    print("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函数调用后返回,返回值。main中foo2调用结束后弹出栈顶,main继续执行print函数调用,弹出栈顶。main函数返回。

示范代码

import dis
def foo1(b,b1=3):
    print("foo1 called",b,b1)
    foo2(2)
def foo2(a):
    pass
print(dis.dis(foo1))

代码字节码

[python@centos7 416]$./foo.py 
  6           0 LOAD_GLOBAL              0 (print)
              3 LOAD_CONST               1 ('foo1 called')
              6 LOAD_FAST                0 (b)
              9 LOAD_FAST                1 (b1)
             12 CALL_FUNCTION            3 (3 positional, 0 keyword pair)
             15 POP_TOP

  7          16 LOAD_GLOBAL              1 (foo2)
             19 LOAD_CONST               2 (2)
             22 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             25 POP_TOP
             26 LOAD_CONST               0 (None)
             29 RETURN_VALUE
None

递归函数

递归函数含义

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

递归性能

  • 循环稍微复杂一些,但是只要不是死循环,可以多次迭代直至算出结果
  • fib函数代码极简易懂,但是只能获得最外层的函数调用,内部递归结果都是中间结果,而且给定一个n都要进行近2n次递归,深度越深,效率越低,为了获取斐波那契数列需要外面在套一个n次的循环,效率更低了
  • 递归还有深度限制,如果递归复杂,函数反复压栈,栈内存很快就溢出了

递归优化

解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以把循环看成是一种特殊的尾递归函数也是可以的,尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包涵表达式。这样编译器或者解释器就可以把尾递归优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况

递归函数总结

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

求斐波那契数列

斐波那契数列解法1

  • 重点内容是f(n)=f(n-1)+f(n-2),因此得出函数为
#!/home/python/.pyenv/versions/3.5.3/bin/python3
#-*- coding: UTF-8 -*-
#created time 2018-04-16
def f(n):
    return 1 if n < 3 else f(n-1)+f(n-2)
for i in range(1,11):
    print(f(i),end=" ")
print()
运行结果:
[python@centos7 416]$./recusion.py 
1 1 2 3 5 8 13 21 34 55 
  • 利用for循环将正序输出斐波那契数列公式

斐波那契数列解法2

#!/bin/python3
#-*- coding: UTF-8 -*-
#created time 2018-04-16
def f(n,x=[1],y=[0]):
    if n > 1:
        x[0],y[0] = x[0]+y[0],x[0]
        print(y[0],end=" ")
    else:
        return x[0]
    return f(n-1,x=[x[0]],y=[y[0]])
print(f(10))
运行结果:
[python@centos7 416]$./recusion.py 
1 1 2 3 5 8 13 21 34 55 
  • 利用函数默认值属性
  • 利用函数体就是关系式,return就是返回结果进行计算

斐波那契数列解法3

#!/bin/python3
#-*- coding: UTF-8 -*-
#created time 2018-04-16
def fib(n,cur=1,pre=0):
    if n > 1:
        cur,pre = cur+pre,cur
        print(pre,end=" ")
    else:
        return cur
    return fib(n-1,cur,pre) #通过位置变量传参
print(fib(10))

求n的阶乘

传统解法

#!/bin/python3
#-*- coding: UTF-8 -*-
#created time 2018-04-16
def f(n):
    if n == 1:
        return 1 
    return n*f(n-1)
print(f(5))
运行结果:
[python@centos7 416]$./fact.py 
120

for循环思路解法

#!/bin/python3
#-*- coding: UTF-8 -*-
#created time 2018-04-15
import dis
def f(n,s=[1]):
    if n ==1:
        print(s[0])          #顶层栈帧pop时,s[0]的值120
        return 
    s[0] *= n
    f(n-1,s=[s[0]])
    return s[0]              #此时最底层栈帧时,s[0]的值为5
print(f(5))
运行结果:
[python@centos7 414]$./n.py 
120
None
  • 此案例能够帮助更深层次理解

尾递归法

#!/bin/python3
#-*- coding: UTF-8 -*-
#created time 2018-04-16
def f(n,num=1):
    if n == 1:
        return num
    num *= n
    return f(n-1,num)    #每次重新创建新的栈,而且这个栈就调用一次即可
print(f(5))
运行结果:
120

序列逆放

将一个数逆序放入列表中,如:1234=>[4,3,2,1]

#!/bin/python3
#-*- coding: UTF-8 -*-
#created time 2018-04-17
def rever(n,s,l=[]):
    if n == 0:
        return l
    num=divmod(int(s),10)
    l.append(num[1])
    return rever(n-1,num[0])
print(rever(4,1234))
运行结果:
[python@centos7 416]$./lst.py 
[4, 3, 2, 1]
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页