递归函数:
定义:在函数内部,可以调用其他函数。如果一个函数在内部调用自身,
这个函数就是递归函数。
实例1(阶乘):
在这里插入代码片def factorial(n):
result=n
for i in range(1,n):
fesult*=i
return result
print(factorial(4))
*********递归*********
def factorial_new(n):
if n==1:
return 1
return n*factorial_new(n-1)
print(factorial_new(3))
实例2(悲波那契数列):
在这里插入代码片def fibo(n):
before=0
after=1
for i in range(n-1):
ret=defore+after
before=after
after=ret
return ret
print(fibo(3))
# ************递归***********
def fibo_new(n): # n可以为零,数列有 [0]
if n < =1:
return n
peturn(fibo_new(n-1) + fibo_new(n-2)
print(fibo_new(3))
print(fibo_new(30000)) # maximum recursion depth exceeded in comparison
在这里插入代码片
0 1 1 2 3 5 8 13 21 34 55
f(3)=1
f(8)=f(7)+f(6)
def fibo(n):
# before=0
# after=1
if n<= 1:
return 1
return fibo(n-1)+fibo(n-2)
print(fibo(8))
在这里插入代码片
def fat(n):
ret=1
for i in range(4,n+1):
ret=ret*i
return ret
print(fat(5))
def fact(n):
if n==1:
return 1
return n*fact(n-1)
print(fact(5))
关于递归的条件:
1.调用自身函数
2.有一个结束条件
但凡是递归可以写的循环都可以解决,
递归的效率在很多时候会很低。
递归函数的优点:是自定义简单,逻辑清晰/理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
递归的特性:
1.必须有一个明确的结束条件
2.每次进入更深一层递归时,问题规模相比上次递归都应有所减少
3.递归效率不高,第递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,所以递归调用的次数过多,会导致栈溢出。)
重要内置函数:
在这里插入代码片
1 filer(function,sequence)
str = ['a', 'b', 'c', 'd']
def fun1(s):
if s != 'a':
return s
ret = filter(fun1, str)
print(list(ret) # ret是一个送代器对象
对sequece中的item依次执行function(item),将执行结果为True的item做成
一个filte object的送代器返回。可以看作是过滤函数。
在这里插入代码片2 map(punction,sequence)
str = [1,2, 'a', 'b']
def fun1(s):
return s + "alvin"
ret = map(fun2, str) #注意filter
print(ret) # map object的送代器
print(list(ret)) # ['aalvin', 'balvin', 'calvin', 'dalvin']
对sequence中的item依次执行function(item),将执行结果组成一个map object送代器返回,
map也支持多个sequence,这个要求function也支撑相应数量的参数输入:
在这里插入代码片ef add(x,y):
return x+y
print (list(map(add,range(10),range(10)))) #[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
3 reduce(function,sequence,staring_value)
from functools import reduce
def add1(x,y):
return x + y
print (reduce(add1,range(1, 101))) ## 4950 (注:1+2+...+99)
print (reduce(add1, range(1, 101), 20)) # 4970 (注: 1+2+。。。+99+20)
对sequence中的item顺序送代调用function,如果有starting_value,还可以作为初始值调用。
在这里插入代码片4 lambda
普通函数与匿名函数的对比:
# 普通函数
def add(a,b):
return a + b
print add(2,3)
# 匿名函数
add = lambda a,b : a + b
print add(2,3)
# =========输出==============
5
5
匿名函数的命名规则,用lamdba关键字标识,冒号 (😃 左侧表示函数接收的参数 (a,b) ,冒号(😃
右侧表示函数的返回值(a+b) 。
因为lamdb在创建时不需要命名,所以,叫匿名函数。
函数式编程:
一、概念(函数式编程)
函数式编程是一种编程范式,我们常见的编程范式有命令式编程(lmperative programming),函数式编程,
常见的面向对象编程是一种命令式编程。
命令式编程是面向计算机硬件的抽象,有变量(对应着储存单元),赋值语句(获取,储存指令),表达式(内存引用和算数运算)和控制语句(跳转指令),一句话,命令式编程就是一个冯诺依曼机的指令程序。、
而函数式编程是面向数学的抽象,将计算机描述为一种表达式求值,一句话,函数式程序就是一个表达式。
函数式编程的本质
函数式编程中的函数这个术语不是指计算机中的函数,而是指数学中的函数,即自变量的映射。也就是说一个函数的值仅决定与函数参数的值,不依赖其他状态。比如y=x*x函数计算机x的平方根,只要x的平方,不论什么时候调用,
调用几次值都是不变的。
纯函数式编程语言中的变量也不是命令式编程语言中的变量,即存储状态的单元,而是代数中的变量,即一个值得名称。
变量的值是不可变的(immutable),也就是说不允许像命令式编程语言中的那样多次给一个变量赋值,比如说在命令式编程语言我们写x = x + 1 ,这依赖可变状态的实时,拿给程序员说是对的,拿给数学家看,却被认为这个等式为假。
函数式语言的如条件语句,循环语句也不是命令式编程语言中的控制语句,而是函数的语法糖,比如在Scala语言中,if else不是语句而是三元运算符,是有返回值的,
严格意义上的函数式编程意味着不使用可变的变量,赋值,循环和其他命令式控制结构进行编程。
函数式编程关心数据的映射,命令式编程关心解决问题的步骤,也是为什么‘函数式编程’叫做‘函数式编程’。
二、实例
假如,现在来到baidu面试,面试官把你number =[2,-5,9,-7,2,5,4,-1,0,-3,8]中的正数的平均值,你肯定写出:
在这里插入代码片# 计算机组中正整数的平均值
cumber =[2,-5,9,-7,2,5,4,-1,0,-3,8]
cout = 0
sum = 0
for i in range(len(number)):
if number[i]>0:
cout += 1
sun += number[i]
print sum,count
if count>0:
average = sum/count
print average
#========输出==========
30 6
5
首先循环列表中的值,累计次数,并对大于0的数进行累加,最后求取平均值。
这就是命令式编程——你要做什么事情,你得把达到目的的步骤详细的描述出来,然后交给机器去运行。
这也正是命令式编程的理论模型——图灵机的特点。一条写满数据的纸带,一条根据纸带内容运动的机器,机器每动一步都需要纸带上写着如何达到。
那么,不用这种方式如何做到呢?
在这里插入代码片number =[2, -5, 9, -7, 2, 5, 4, -1, 0, -3, 8]
positive = filter(lambda x: x>0, number)
average = reduce(lambda x,y: x+y, positive)/len(positive)
print average
#========输出===========
5
这段代码最终达到的目的同样是求取正数平均值,但是它得到的结果的方式和之前有着本质的差别:通过描述一个列表->
正数平均值的映射,而不是描述从列表得到正数平均值应该怎么做 来达到目的。
再比如,求阶乘
通过Reduce函数加lambda表达式实现阶乘是如何简单:
在这里插入代码片
from functools import reduce
print (reduce(lambda x,y: x*y, range(1,6)))
又比如,map()函数加上lambda表达式(匿名函数)可以实现更强大的功能:
在这里插入代码片squares = map(lambda x : x*x ,range(9))
print (squares) # <map object at 0x10115f7f0>送代器
print (list(squarse)) # [0, 1, 9, 16, 25, 36, 49, 64]
三、函数式编程有什么好处呢?
1、代码简洁,易懂。
2、无副作用
由于命令式编程语言也可以通过类似函数指针的方式来实现高阶函数,函数式的最主要的好处主要是不可变性带来的。
没有可变的状态,函数就是引用透明(Referential transparency)和没有副作用(No Side Effect)。