python函数式编程
off999 2024-11-18 15:39 37 浏览 0 评论
python列表推导式
列表推导式的语法格式:[f(x) for x in L],其中 f(x) 表示一个函数,作用于列表 L 的每一个元素。所以整体来看,就是将函数f映射到列表 L 中的每个元素上。其返回值是一个新的列表
例如,将列表中每个元素加1
[x+1 for x in [10,20,30]]
1L 也可以是一个字符串
[x for x in 'string']
1当然也可以加if 的判断条件,用于对元素进行条件判断。
一般语法格式为:[f(x) for x in L if ...],返回值是哪些符合 if 条件的元素所组成的新列表。
[str for str in ['java','javascript', 'python'] if len(str) > 4]
12lambda表达式
lambda表达式是根据丘奇的lambda演算定义的,它是最纯粹的函数式编程。
语法格式为:(lambda arg : state)(arg)。其中 lambda 是一个关键字,: 前的 arg 是参数,而 state 是要执行的语句,第二个括号里的 arg 是外部传入的参数。
可以看下下面的例子 :
(lambda x:x+1)(10)
1当然可以接收任意的参数:
(lambda x,y : x+y)(10,20)
1如果要进行逻辑判断呢?当然也是可以的,不过有一点是需要注意的,那就是不能使用 if 语句,因为它并不是一个表达式。纯粹的函数式编程是不能有 if 存在的。
(lambda x : x % 2 == 0)(20)
1如果想到一个列表进行筛选,那岂不是也需要判断条件吗?让我们再来看一个 :
加入我们想筛选出一个列表中的偶数,返回一个符合条件的所有元素组成的新列表,可以这样做
list(filter(lambda x : x % 2 == 0,[10,20,1,2,3,8]))
1看到里面使用到了filter函数,我们就顺便说一下filter。
filter
filter 是用来进行筛选的函数,接收一个函数和一个列表,在上面的例子中,filter(func, list),我们传入的是一个 lambda表达式,它是一个匿名函数,[10,20,1,2,3,8]即是这个lambda要处理的元素集合,而filter 将函数映射到了列表中的每一个元素上,最后我们将返回的结果转化为一个list列表。
看一个接收具名函数的例子 :
def isOdd(x):
return x%2 == 0
list(filter(isOdd,[1,2,3,4,5,6]))
1234像这种简单的函数,也许使用匿名函数会更简洁,也不影响代码的可读性。但需要注意的是,filter 所接收的函数只能接收一个参数,且需要返回一个布尔值,这个函数本身被称为谓词,我认为是因为它执行一个动作,所以称为谓词。
map
通过 filter 很容易想到,如果我们不是做函数,而是做一个集合的完全映射呢?即把一个集合通过一个函数完全映射为了另外一个函数,这样的说法似乎很熟悉,是不是很像中学时候学到的函数,f(x) -> y,python的内置函数map可以帮我们做到这一点。
同样的,它接收一个单参数函数,但是这个函数不需要像filter那样返回布尔值,事实上我们完全可以用map替代filter,但是filter本身值得单独存在。
看下面一个例子 :
def add(x):
return x + 1
list(map(add, [1,2,3]))
1234同样我们可以将具名函数 add 替换为匿名函数,我们使用lambda来做到这一点:
list(map(lambda x : x+1, [1,2,3]))
1我们也可以传入内置单参函数:
list(map(len, ['java', 'javascript', 'python']))
1reduce
假如我们要将一个列表中的所有元素相加求和,应该怎么做?嗯,可以这样子:
sum([1,2,3,4,5])
1但,我相信你知道我不是这个意思。我想说的是,如果我们使用reduce帮忙我实现呢?
reduce(lambda x,y : x+y, [1,2,3,4,5])
1发现什么不同了么?对比前面的filter和map来说,它接收的是两个参数,是的,只能接收两个参数的函数。为什么这样呢?因为它的功能是做规约,即将列表中所有的元素规约为一个值。
它的工作过程大概是这样的:reduce 从列表中获取前两个元素,并使用传入的函数将其规约为单个值;然后这一过程继续进行,将得到的单个值与列表中的下一个元素结合,再次规约为单个值,这个过程一直持续,一直到仅剩单个值。
mapReduce
map 和 reduce 组合在一起会组成强大的功能,先使用 map 转换列表中的值,然后使用 reducce 将结果合并为单个值。
reduce(reduceFunction, map(mapFunction, yourList))
1def add(x, y):
return x + y
reduce(add, map(lambda x : x + 1, [1,2,3,4,5]))
1234我们可以直接封装为 mapReduce 函数:
def mapReduce(mapFunction, reduceFunction, yourList):
return reduce(reduceFunction, mapFunction(yourList))
12递归
递归和函数调用
递归就是这样,让人以为清楚又不清楚,在解释什么是递归之前,先说说什么不是递归。
def func1(x):
result = 2*x
return result
def func2(x):
y = x + 1
z = func1(y)
return x + y + z
print(func2(5))
12345678910函数 func2 里调用了 func1,请问这是递归吗?这不是,那函数调用和递归有关系吗?答案是有关系。
让我们再来看一个递归的经典例子:斐波那契数列。
从斐波那契数列说开来
斐波那契数列:
def factorial(n):
if n <= 1:
return 1
return n * factorial(n-1)
factorial(5)
1234567所以,递归就是一个函数调用另一个函数,只是恰好被调用的函数与调用函数的名称相同而已。
但是我们如果对一个问题进行递归算法的设计呢?
我们想一想,一般的函数调用,函数A调用函数B,调用完之后就结束了。就算是有多层嵌套,那么函数A调用函数B,函数B调用函数C,函数C再调用函数D,调用到最后还是会结束,然后逐一向外返回结果。而递归这种自己调用自身的呢?在没有任何限制的情况下,会无限的进行嵌套调用,不会停止,导致爆栈。可是应该何时判断递归调用结束,避免爆栈呢?当然是在函数自身内部进行判断,也是到达所谓的基本情况。因此使用递归最基本的,也是非常重要的地方,就是找到基本情况。
我们来分析一下这个斐波那契数列递归算法的基本情况:
到哪里停止?像我们上面的例子来说,输入5,递归调用自身,需要f(4),参数 n 的限制是什么?到了这个限制的边界上,应该如何处理?我们看到它的基本情况是 n=1,只有这样一个基本情况。然后递归函数只有一个基本情况吗?针对斐波那契来说是这样的,我们来看另外一个例子,也是常说的打家劫舍问题。
认真说说打家劫舍问题
小偷去偷东西,他所带的包只能放下42个单位的东西,他可以偷到的东西重量分别是5、10、18、23、30、45,在这种情况下,小偷所能够偷到的货物重要最多是多少呢?
我们直观来看,如果他选择18和23,那么总重量为41,是目前最好的组合。但是他所能偷到的东西增加了一个2单位的物品,情况就不一样了,这时候他选择2、10、30三种物品,总重量刚好就是42。
在这个例子里,就存在两种基本情况,一个是小偷所能带走的容量,另一个是他所选择物品的总重量。我们来设计这样一个函数,他接收两个容量和总重量两个参数,返回能够选择的物品之间的最大重量:
subset(42,[5,10,18、23,30,45]) // 41
subset(42,[2,5,10,18,23,30,45]) // 42
123如何设计这样一个递归函数呢?我们从考虑基本情况说起。先考虑容量,容量最小是什么情况呢?
容量最小肯定只能是0,虽然现实中肯定不可能小于0,但是作为参数判断,肯定要考虑小于0的情况,因此当输入的容量 <= 0 的时候,直接返回0,容量为0根本没法偷。
因此:
if capacity <= 0:
return 0
12这时候我们来考虑第二种情况,如果能偷的物品列表为空呢?也是没得偷的情况,这种情况的话能偷的情况肯定也是0,因为没得偷:
if items == []:
return 0
12这两种情况肯定是并列的,所以我们可以放在一起:
if capacity <=0 or items == []:
return 0
12这就是基础的情况,接下来我们来考虑如何设计递归呢?
我们这样来考虑,面对一个一个的物品(因为列表是有顺序)的,所以对于当前物品,我们面临两种选择,偷或者不偷。首先有一种特殊的情况是需要考虑的,那就是如果第一个物品的重量就是大于容量的话,我们的选择是不偷。
if capacity <=0 or items == []:
return 0
first = items[0]
rest = items[1:]
if first > capacity:
return subset(capacity, rest)
12345678如果第一个物品的重量是等于容量的话,我们的选择是直接偷:
if capacity <=0 or items == []:
return 0
first = items[0]
rest = items[1:]
if first > capacity:
return subset(capacity, rest)
if first == capacity:
return first
1234567891011如果第一个物品重量小于剩余容量的话,则我们要考虑偷或者不偷,但是事实上却没有这么简单。
因为我们的目标是偷到的物品总重量最大,因此可能存在这两种不同的情况:
1.假如剩余容量capacity为10,物品重量列表为items[8,4,6],那么这种情况下应该不偷第一个物品,我们选择偷后两种物品,这样能够偷到的总重量为10。
2.但是如果同样容量为10的情况下,物品重量列表为[4,8,6],这种情况下我们应该取第一个和第三个物品,这样子偷的物品重量最大。
所以不能只看当前物品重量是否小于容量来决定是不是要偷,而是要比较不同的情况下偷到的总重量。
你或许会有这样一个疑问,那我岂不是手动要比较每一次?那不是需要全部比较完才知道结果?事实上递归是这样运作的:本次在A偷与不偷,我们全部走一次,偷的情况下,偷了以后再看剩余的容量和剩余的物品列表,再继续看当前物品偷不偷,然后这时候又是两种情况都进行,偷的情况下走了一个分支,继续下一次偷与不偷的判断,然后不偷的情况走一个分支,继续下一次偷与不偷的判断。到了最后基本的情况开始一层一层的返回,返回每一次偷的结果和不偷的结果,对比得到偷与不偷,这个结果再继续返回,在对比上一层偷与不偷的结果,只到返回到最外层,判断最初的那个物品的选择,偷与不偷的情况。
是不是有点烧脑 ?递归就是这样,你以为你理解了,但细细考虑,你发现你还是没考虑情况,总是把自己陷进去,这可能是由于计算机是用栈来记录递归调用情况的,而我们的大脑并不是的原因吧。
让我们用代码来描述这个过程:
if first < capacity:
use = first + subset(capacity - first, rest)
lose = subset(capacity, rest)
return max(use, lose)
1234567由于每一次都有两种情况,所以如果物品列表items中有n个物品,那么最多可以产生2的n次方次调用。
这里最难理解的,可能是每一次的两种选择,需要再假定当前取或不取情况下两种选择分别的情况,然后这个过程持续进行,可能脑中很难去演义这整个过程。我觉得好的方式是,不在大脑中去演示这种一直递归的情况,因为我们的脑子没有一个栈来保存调用记录,而是理解清楚每一个选择都需要简化的一个选择,直到最基本的情况。
让我们来看下全部的代码:
def subset(capacity, items):
if capacity <=0 or items == []:
return 0
first = items[0]
rest = items[1:]
if first > capacity:
return subset(capacity, rest)
if first == capacity:
return first
if first < capacity:
use = first + subset(capacity - first, rest)
lose = subset(capacity, rest)
return max(use, lose)
1234567891011121314151617181920我们来验证一下:
maxVal = subset(10,[3,4,2,9,10])
print(maxVal)
12我们来总结一下:
首先递归就是一个函数调用另一个函数,只不过是函数调用的恰好是它自身。我们在设计递归函数时,使用的策略是解决较小版本的问题让我们更接近解决原来版本的问题,这个较小版本的问题更加接近基本情况。我们需要清楚的就是:这个问题必须处理的最简单或者说最基本的输入是什么?
典型的递归函数应该有两个主要部分:
(1)基本情况:针对函数的“最简单”参数返回的值,这种情况下已经没有自相似性的存在。
(2)递归情况:这是较小版本的问题的解。利用更小或者更简单的参数来调用该函数,然后以某种方式利用较小问题的解来解决原始问题。
另外递归的模式可以总结为:
(1)将输入分解为first和rest,或者类似的东西,即让问题简化
(2)对rest部分进行递归
(3)按照特定于问题的方式,将递归调用的结果和rest组合起来
(4)记住并推理基本情况,有时候可能需要几种基本情况。
那么递归难道仅仅只是如此吗?我的意思是说,难道所有的递归函数都只需要考虑两种情况吗?当然不是这样,接下来我们就看一个问题。
字符串转化的最小距离:给定两个字符串,计算将第一个字符串转换成第二个字符串所需要的最少操作。可以采用三种操作:替换、插入和删除。
举个例子,假如给定的两个字符串分别是 alice 和 cmice,我们要将 alice 转换成 cmice,对比两个字符串,对于alice的第一个字符a,我们可以考虑将在头部插入一个c,也可以直接将a替换成c,也可以将a删除。
我们来分析这三种情况:
如果在头部插入c,那么第一个字符串(以下称为字符串A)变为calice,那么就将问题转化为了将字符串calice变为第二个字符串cmice(以下称为字符串B)。如果将a替换为c,那问题就转化为了将字符串clice变为字符串cmice。如果将a删除,那么问题就转化为了将字符串lice变为字符串cmice。
因此每次我们有三种选择,相比上面的例子相对复杂了一些。
这时候我们来看下这个问题所需要考虑的基本问题:
我们所接收的参数有两个,分别是字符串A和字符串B,那么最基本的情况是字符串为空。假如A为空,B不为空,那么A变成B所需要的步骤即为B的长度,将B中每一个字符都依次插入到A;如果A不为空,B为空,那么A变成B所需要的步骤即为A的长度,将A中每一个字符都删除,直至为空;那么如果A和B同时为空的话,那么不需要任何操作,步骤为0。
我们来写下代码:
def translate(s1,s2):
if len(s1) == 0:
return len(s2)
if len(s2) == 0:
return len(s1)
123456考虑完基本的情况,我们来考虑递归的情况:
我们要逐个对比两个字符串的每个字符是否相同,如果相同,那么直接删除当前字符,然后将问题简化为去掉当前字符的情况。
def translate(s1,s2):
if len(s1) == 0:
return len(s2)
elif len(s2) == 0:
return len(s1)
elif s1[0] == s2[0]:
return translate(s1[1:],s2[1:])
1234567如果字符串当前所比较的两个字符不相同呢?我们可以有三种操作,意味着我们有三种选择,这时候可以像上面的例子一样,我们全部尝试,看哪一个步骤最少。但是我们是要递归进行,在递归时候我们要考虑的就是参数如何变化,每一次的结果是怎样的。上面已经分析过了,我们再来回顾一下:
(1)如果在头部插入c,那么第一个字符串(以下称为字符串A)变为calice,那么就将问题转化为了将字符串calice变为第二个字符串cmice(以下称为字符串B)。当然可以不体现这一步,B去掉首字符,相当于插入之后A和B都去除首字符了,即tranlate(s1,s2[1:]),操作增加了一次(插入操作)。
(2)如果将a替换为c,那问题就转化为了将字符串clice变为字符串cmice。这时候字符串A和B都可以将当前字符(首字符)删除,即tranlate(s1[1:],s2[1:])`,操作增加了一次(修改操作)。
(3)如果将a删除,那么问题就转化为了将字符串lice变为字符串cmice。这时候A字符串都能将当前字符(首字符)剔除,而B字符串不变,即tranlate(s1[1:],s2),操作增加了一次(删除操作)。
转化为代码:
insert = 1 + translate(s1,s2[1:])
replace = 1 + translate(s1[1:], s2[1:])
delete = 1 + translate(s1[1:],s2)
return min(insert,replace,delete)
1234我们看下完整的代码:
def translate(s1,s2):
if len(s1) == 0:
return len(s2)
elif len(s2) == 0:
return len(s1)
elif s1[0] == s2[0]:
return translate(s1[1:],s2[1:])
else:
insert = 1 + translate(s1,s2[1:])
replace = 1 + translate(s1[1:], s2[1:])
delete = 1 + translate(s1[1:],s2)
return min(insert,replace,delete)
123456789101112我们来验证一下:
print(translate('alcei','clsgt'))
1- 上一篇:用Python求和代码
- 下一篇:python经典案例:求1到100之和
相关推荐
- 笔记本电脑选哪个品牌比较好
-
1、苹果APPLE/美国2、戴尔DELL/美国3、华为HUAWEI/中国4、小米MI/中国5、微软Microsoft/美国6、联想LENOVO/中国7、惠普HP/美国8、华硕ASUS/...
- 10系列显卡排名(10系显卡性能排行)
-
十系显卡指NVIDIAGeForce10系列,是英伟达研发并推出的图形处理器系列,被用以取代NVIDIAGeForce900系列图形处理器。新系列采用帕斯卡微架构来代替之前的麦克斯韦微架构,并...
-
- 最新win7系统下载(windows7最新版本下载)
-
最简单的方法就是,下载完镜像文件后,直接把镜像文件解压,解压到非C盘,然后在解压文件里面找到setup.exe,点击运行即可。安装系统完成后,在C盘找到一个Windows.old(好几个GB,是旧系统打包在这里,垃圾文件了)删除即可。扩展资...
-
2026-01-15 06:43 off999
- 哪个电脑管家软件好用(哪个电脑管家好用些)
-
腾讯电脑管家吧,因为这个是杀毒和管理合一的,占用内存小,因此显得更为简洁,使电脑运行更加流畅此外电脑诊所,工具箱以及4+1的杀毒模式让腾讯电脑管家也收到了广泛的关注4+1杀毒引擎,管家反病毒引擎、金山...
- 怎么进入win7安全模式(怎么进入win7安全模式界面)
-
方法如下:1、首先进入Win7系统,然后使用Win键+R组合键打开运行框,输入“Msconfig”回车进入系统配置。2、在打开的系统配置中,找到“引导”选项,然后单击,选择Win7的引导项,然后在“安...
- 怎么分区固态硬盘(怎样分区固态硬盘)
-
固态硬盘的分区方法与传统机械硬盘基本相同,以下是一个简单的步骤:1.打开磁盘管理工具:在Windows操作系统中,按下Win+X键,选择"磁盘管理"。或者打开控制面板,在"...
-
- 笔记本声卡驱动怎么下载(笔记本如何下载声卡)
-
1、在浏览器中输入并搜索,然后下载并安装。2、安装完成后打开360驱动大师,它就会自动检测你的电脑需要安装或升级的驱动。3、检测完毕后,我们可以看到我们的声卡驱动需要安装或升级,点击安装或升级,就会开始自动安装或升级声卡了。4、升级过程中会...
-
2026-01-15 05:43 off999
- win10加快开机启动速度(加快开机速度 win10)
-
一、启用快速启动功能1.按win+r键调出“运行”在输入框输入“gpedit.msc”按回车调出“组策略编辑器”?2.在“本地组策略编辑器”依次打开“计算机配置——管理模块——系统——关机”在右侧...
-
- excel的快捷键一览表(excel的快捷键一览表超全)
-
Excel快捷键大全的一些操作如下我在工作中经常使用诸如word或Excel之类的办公软件。我相信每个人都不太熟悉这些办公软件的快捷键。使用快捷键将提高办公效率,并使您的工作更加轻松快捷。。例如,在复制时,请使用CtrI+C进行复制,...
-
2026-01-15 05:03 off999
- 华硕u盘启动按f几(华硕u盘装系统按f几进入)
-
F8。1、开机的同时按F8进入BIOS。2、在Boot菜单中,置secure为disabled。3、BootListOption置为UEFI。4、在1stBootPriority中usb—HD...
- 手机云电脑怎么用(手机云端电脑)
-
使用手机云电脑,您首先需要安装相应的云电脑应用。例如,华为云电脑APP。在安装并打开应用后,您将看到一个显示器的图标,这就是您的云电脑。点击这个图标,您将被连接到一个预装有Windows操作系统和必要...
- ie11浏览器怎么安装(ie11浏览器安装步骤)
-
如果IE浏览器11版本你发现无法正常安装,那么很可能是这样几个原因,一个就是电脑的存储空间不够到时无法安装,再有就是网络的问题,如果没有办法安装的话就不要再安装了,本身这个IE浏览器并不是多好用,你最...
- 台式机重装系统win7(台式机怎么重装win7)
-
下面主要介绍两种方法以重装系统:一、U盘重装系统准备:一台正常开机的电脑和一个U盘1、百度下载“U大师”(老毛桃、大白菜也可以),把这个软件下载并安装在电脑上。2、插上U盘,选择一键制作U盘启动(制作...
- 字母下划线怎么打出来(字母下的下划线怎么去不掉)
-
第一步,在电脑上找到文字处理软件WPS,双击即自动新建一个新文档。第二步,在文档录入需要处理的字母和数字,双击鼠标或拖动鼠标选择要处理的内容。第三步,在页面的左上方的横向菜单栏,找到字母U的按纽,点击...
欢迎 你 发表评论:
- 一周热门
-
-
抖音上好看的小姐姐,Python给你都下载了
-
全网最简单易懂!495页Python漫画教程,高清PDF版免费下载
-
飞牛NAS部署TVGate Docker项目,实现内网一键转发、代理、jx
-
Python 3.14 的 UUIDv6/v7/v8 上新,别再用 uuid4 () 啦!
-
python入门到脱坑 输入与输出—str()函数
-
Python三目运算基础与进阶_python三目运算符判断三个变量
-
(新版)Python 分布式爬虫与 JS 逆向进阶实战吾爱分享
-
失业程序员复习python笔记——条件与循环
-
系统u盘安装(win11系统u盘安装)
-
Python 批量卸载关联包 pip-autoremove
-
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)
