函数的执行过程、递归函数、匿名函数

函数的执行过程、递归函数、匿名函数

函数的执行过程

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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)


print("main called")
foo1(100,101)
foo2(200)
print("main ending")
main called
foo1 called 100 101
foo3 called 200
foo2 called 200
main ending

以上代码执行流程:

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

递归函数

递归函数的含义

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

递归函数的性能

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

递归函数的优化

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

递归函数的总结

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

递归函数的使用案例

1. 斐波那契数列
解法1:重点内容是 f(n) = f(n-1) + f(n-2)

1
2
3
4
5
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=' ')
1 1 2 3 5 8 13 21 34 55 

解法2:利用函数体就是关系式,return就是返回结果进行计算

1
2
3
4
5
6
7
8
9
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]])

f(10)
1 1 2 3 5 8 13 21 34 




55

2.求n的阶乘

1
2
3
4
5
6
def f(n):
if n == 1:
return 1
return n * f(n-1)

f(5)
120

求n的阶乘尾递归法

1
2
3
4
5
6
def f(n,num=1):
if n == 1:
return num
num *= n
return f(n-1,num)
f(5)
120

匿名函数

匿名函数就是没有名字的函数,但是,如果过函数没有名字,要如何定义? 要如何调用? 要如何使用?  
  • Python借助lambda表达式构建匿名函数
  • 格式:
1
2
3
4
5
6
7
8
9
10
11
# **lambda 参数列表:表达式
print( lambda x: x**2 )
print( (lambda x:x**2)(4) )

foo = lambda x,y:(x + y)**2
print( '{} {}'.format('foo(2,1):',foo(2,1)) )

def foo(x,y): # 相同的效果
return (x + y) ** 2

print( '{} {}'.format('foo(2,1):',foo(2,1)) )
<function <lambda> at 0x000001EBD826E6A8>
16
foo(2,1): 9
foo(2,1): 9

匿名函数的定义

  • 匿名函数使用 lambda关键字来定义
  • 参数列表不需要小括号
  • 冒号是用来分割参数列表和表达式
  • 不需要使用return,表达式的值,就是匿名函数的返回值
  • lambda表达式(匿名函数)只能写在一行上面 ,被称为单行函数

匿名函数的用途

在高阶函数传参时,使用lambda表达式,往往能简化代码
1
2
3
4
5
6
print((lambda :0)())
print((lambda x,y=3:x+y)(5))
print((lambda x,y=3:x+y)(5,6))
print((lambda x,*,y=30:x+y)(5))
print((lambda x,*,y=30:x+y)(5,y=10))
print((lambda *args:(x for x in args))(*range(5)))
0
8
11
35
15
<generator object <lambda>.<locals>.<genexpr> at 0x000001EBD82AB2A0>
-------------本文结束感谢您的阅读-------------
0%