python 函数式编程及递归

函数式编程是一种古老的编程模式,即用函数(计算)来表示程序,用函数的组合来表达程序组合的思维方式,最开始受到学术界的热捧,近年来开始在业界被投入使用。因此,越来越多的高级语言都加入对函数式编程的支持,python同样如此。

lambda表达式

lambda表达式可以创建一个匿名函数,对于一些使用频率低、结构简单的函数,使用lambda函数可以更为简洁

lambda语法

使用冒号(:)分割函数的参数及返回值:左边放函数的参数,若有多个参数用“,”号隔开,右边是函数返回值

lambda 参数1, 参数2 : 参数返回值

示例:

a = lambda x , y : 2 * x + y 
a(3,4) #输出结果:10

若使用常规写法,则复杂一些:

def a(x,y):
    return 2 * x + y
a(3,4) #输出结果:10

除了简化函数之外,也可以将闭包转化为lambda表达式:

def funX(x):
    return lambda y : 2 * x + y
a = funX(5)
print(a(3)) #输出结果:13

总结

  1. lambda 定义了一个匿名函数
  2. lambda 并不会带来程序运行效率的提高,只会使代码更简洁,同时可能会降低一定可读性
  3. 如果可以使用for…in…if来完成的,坚决不用lambda
  4. 如果使用lambda,lambda内不要包含循环,如果有,尽量定义函数,使代码获得可重用性和更好的可读性

filter()

filter顾名思义,是一个筛选器。那这个是筛选什么呢?举个例子

filter(lambda x:x % 3 == 0, [i for i in range(100)])

这行代码先用列表表达式生成0-99的列表,然后将99个参数依次代入,将符合x%3==0(3的倍数)条件的参数筛选出来。运行结果为[3,6,9…,99]

filter()语法

filter(函数,参数集)

函数的返回值需要为True或False的布尔型数值或者是0 1。而参数集可以是元组,也可以是列表。
若函数为None,则直接筛选参数集

filter(lambda x:x % 3, (1,3,5,7,9,11,13,15))

该代码则返回除以3余数为1的数值

map()

map可以理解为映射。将参数集中的参数依次传递给函数加工,生成新的返回值集。
语法结构和filter类似:

map(函数,收集参数)

收集参数中支持多个可迭代对象,即多个参数集。依次从参数集中选出一个参数生成元组传递给函数。若各参数集长度不一致,则以较短的为准。举个例子:

map(lambda x, y : x + y, [1,2,3],[4,5,6,7,8]) 

在此处传递了两个参数集,实际上相当于将(1,4) (2,5) (3,6)传递给函数加工

那么,用通俗点的话就是:map是将众多参数一起给函数加工,而常规的for循环等则是一个一个给函数加工。那么,map到底是一批一批给函数加工,还是一次性全给函数加工?速度上到底差距多少呢?

from time import time
def mult(x):
    return x*x
max = 100000000

a = [i for i in range(max)]
t0 = time()
for i in a:
    mult(i)
t1 = time()
print((t1-t0)*1000) #输出结果:26727.608680725098

t2 = time()
map(mult,a)
t3 = time()
print((t3-t2)*1000) #输出结果:0.0

在该代码中(运行结果由电脑性能决定),定义了一个返回参数平方值的函数,同时为了放大结果差异,生成了一个0-99999999的列表,合计1亿个元素。一个用循环的写法,一个用map的写法,用time模块来记录两个方式运行的时间。

在程序中,可以很明显看出1亿个元素循环加工的话,需要运行的时间远远超过map方式。即使放大了1000倍仍然无法看到map运行的时间。而使用比值print((t1-t0)/(t3-t2))的话,竟然抛出ZeroDivisionError异常,即map的运行时间极短,短到time()方法几乎无法记录。

从逻辑上,整个流程大致如下:
【循环】计时——传入参数——加工——传入参数——加工——……——停止计时——输出时间
【map】计时——传入参数——加工——停止计时——输出时间
可以看出,一个是一个一个加工,一个是同时加工

递归

递归(英语:Recursion),又译为递回,在数学与计算机科学中,是指在函数的定义中使用函数自身的方法。递归一词还较常用于描述以自相似方法重复事物的过程。例如,当两面镜子相互之间近似平行时,镜中嵌套的图像是以无限递归的形式出现的。也可以理解为自我复制的过程。【by维基百科】

谢尔宾斯基三角形就是一个递归形成的图形
在这里插入图片描述
递归通俗点说,就是函数运行中再次调用函数。就像在接力赛中,自己的一棒跑完了又传给自己接着跑。那么递归在程序中如何体现呢?
求阶乘:

def factorial(n):
    if n == 1:
        return 1
    else:
        return n*factorial(n-1)
number = 5
print(factorial(number)) #输出结果:120

可以看到,在当n!=1时,函数的返回值中再次调用了自己,而if语句则是用来跳出递归的条件。假如我们在程序中,不添加跳出语句,那么递归会一直运行下去,直到把内存全部消耗完,这样的程序无疑是失败的。在python中,对于递归深度是有默认限制的,所以并不会真的把内存全部消耗完才停止,当递归到指定限制的深度时,程序就会自动停止。若是应用在爬虫方面,当爬虫需要爬很深时,就需要自行设置递归深度了:

import sys
sys.setrecursionlimit(10000) #设置递归深度为10000

虽然可以自行设置递归深度,但是当递归深度很大时,同样可能出现过度消耗资源而崩溃。可以通过ctrl+C强行停止

斐波那契数列

在数学中有一个数列叫做“斐波那契数列”,即该位置的数等于前两个数的和。该数列是由兔子繁殖引出的:假设在一个地方有一对兔子,且每只兔子都不会死去,这里也没有兔子的天敌;兔子出生两个月后就有了繁殖能力,每个月都能生一对兔子,那么n月之后有多少对兔子?
数字表现形式为:1,1,2,3,5,8,13,21,34……
这个问题用常规写法,需要创建三个变量,分别表示前两个月,前一个月,本月的兔子数,最后通过条件判断,通过循环实现计算。但是使用递归,整个问题就很简单了:

def fab(n):
    if n < 1:
        print('n>=1')
        return -1
    if n == 1 or n == 2:
        return 1
    else:
        return fab(n-1) + fab(n-2)
print(fab(12)) #输出结果:144

事实上,使用递归解决问题的时间,并不比迭代快,甚至若cpu性能一般的话,差距会更大。显然,在这种逻辑比较明显的问题上,递归比迭代间接,但是运行时间反而更长。

汉诺塔难题

但是当碰到汉诺塔难题,常规写法就非常复杂了。汉诺塔即三根杆,第一根杆套有n个圆盘,要求所有圆盘叠放都符合小盘在上大盘在下,一次只能移动一个圆盘,将第一根杆的所有圆盘移动至第三根杆。显然,这种问题用常规解法一脸懵(反正我是一脸懵),但是用迭代就可以比较简单实现了:
假设三根杆为A,B,C,移动第n个圆盘可以分解为三步:
(1)将前n-1个圆盘移动至B杆
(2)将第n个圆盘移动至C杆
(3)将B杆的前n-1个移动至C杆
那么,移动n-1个圆盘,也可以分解为三步:
(1)将前n-2个圆盘移动至C杆
(2)将第n-1个移动至B杆
(3)将C杆的前n-2个移动至B杆
那么问题很明显了,移动第n个圆盘都有固定的步骤。所以我们只需要正确地传递n,A,B,C参数,就可以通过迭代的方式实现汉诺塔的移动:

def hanoi(n,a,b,c):
    if n == 1:
        print(a,'->',c) #若只有一个,则直接移动
    else:
        hanoi(n-1,a,c,b) #将前n-1个从a移动至b
        print(a,'->',c) #移动第n个
        hanoi(n-1,b,a,c) #将前n-1个从b移动至c
hanoi(4,'A','B','C')

函数中的a表示第n个的起始位置,c表示第n个的终止位置,b表示中间存放n-1个的位置

所有的循环都能写成递归(加上跳出条件即可),但是所有的递归不能都写成循环

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值