09 函数式编程之一


目录

  • 匿名函数(lambda表达式)
  • 三元表达式
  • 高阶函数

    • map
    • reduce
    • filter
  • 函数式编程与命令式编程小结
  • 装饰器

上一章节的闭包,是函数式编程思维的一种应用。这一章节,将介绍函数式编程的其他应用方。

一、匿名函数(lambda表达式)

顾名思义,没有名字的函数。叫法不太好,没有体现表达式的属性。
严谨点,最好应该叫lambda表达式。
本质:函数

1.定义

普通的有名字的函数匿名函数(lambda表达式)
定义方式def f():
....pass
lambda parameter_list:expression
例题def add():
....return x + y
lambda x,y:x + y
特点 没有了add的函数名
不需要 return 关键字
调用方式add(1,2)
-->3
f = lambda x,y:x + y
....print(f(1,2))
-->3

上述lambda表达式的调用,只是将其赋值给一个变量f。是无意义的调用,因为这不又跟左边的调用没区别了么,也就是说f = add
那真正的调用方式,后面会讲。

未来的我:
将lambda表达式,作为参数,传递进map、reduce、filter函数中。它其实是个算子,相当于命令式编程中的def功能,只不过简化为只有参数、内部实现。

2.注意事项

lambda表达式中的第二个位置,只能是表达式,不能是代码块。

f = lambda x,y:x + y     #第二个位置,只能是表达式
print(f(1,2))

-->
3
f = lambda x,y:a = x + y     #第二个位置,改成代码块  
print(f(1,2))

-->
error ❌
七月老师:
在C#中,匿名函数与lambda表达式,其实是两个不同的东西。
而在python中,指的是一个东西。

二、三元表达式

本质:表达式版本的if else逻辑
功能:用于条件判断
场景:常作为表达式,用在lambda表达式的第二个位置

例题:
有两个数字x,y,如果x>y,就返回x。否则,返回y。用三元表达式编写?

其他语言常用的经典写法python语言
对比x > y ? x : y条件为真时返回的结果 if条件判断 else 条件为假时返回的结果
x if x > y else y

由于只是个表达式,非完整的代码。所以,通常对表达式会有一个执行过程,用变量r来接受其结果。

完整的写法:

x = 2
y = 1
r = x if x > y else y
print(r)

-->
2

三、高阶函数

(一)map

之前学习的闭包,是函数式编程的一种应用,推荐在写框架、写类库时使用,不推荐在业务逻辑中使用。
然而,函数式编程思维最有体现的,却是map函数。推荐多多使用。

1.定义

结构:

map(func,*iterables)      #*iterables:可重复、可迭代、可遍历的一组数据,序列

2.例题:
求列表[1,2,3,4,5,6,7,8]下面每个元素的平方,并以列表的形式呈现出来?

写法1:
用for循环

list_x = [1,2,3,4,5,6,7,8]
list_y = []              #定义一个空列表,作为接受新元素的空葫芦

def square(x):
    return x * x

for x in list_x:
    y = square(x)
    list_y.append(y)    #⭐利用的是内置函数appendix,将每次生成的新元素,传入添加到上述空列表中

print(list_y)

-->
[1, 4, 9, 16, 25, 36, 49, 64]

写法2:

list_x = [1,2,3,4,5,6,7,8]

def square(x):
    return x * x

r = map(square,list_x)    #map会把集合中的每个元素,都传入到square中去,并接受square的返回结果
print(r)

-->
<map object at 0x000001C1CC22AE30>      #得到的是map对象

用内置函数list(),将map对象里面的元素读取出来:

list_x = [1,2,3,4,5,6,7,8]

def square(x):
    return x * x

r = map(square,list_x)
print(list(r))

-->
[1, 4, 9, 16, 25, 36, 49, 64]

小结:
map函数,到底有什么用?

  • 从数学角度:
    就是个映射
始:[1,2,3,4,5,6,7,8]

末:[1, 4, 9, 16, 25, 36, 49, 64]
  • 从好理解的角度:
    就当作执行了一次for循环,for循环内部都调用了square函数。

3.map函数与lambda表达式的结合

这是更能体现map优势的用法
即,用lambda表达式来代替上述写法中的square函数。

(1)写法3:

list_x = [1,2,3,4,5,6,7,8]
r = map(lambda x:x * x,list_x)     #只用了两行代码,比for循环简洁、优美、易懂

print(list(r))

-->
[1, 4, 9, 16, 25, 36, 49, 64]

这才是真正的用函数式编程思维来解决问题。

(2)以上简单是因为输入的参数只有一个,那如果是多个参数呢?

list_x = [1,2,3,4,5,6,7,8]
list_y = [1,2,3,4,5,6,7,8]

r = map(lambda x,y:x * x + y,list_x,list_y)    
             #lambda表达式与map函数的参数位置,都要加上新的参数  

print(list(r))

-->
[2, 6, 12, 20, 30, 42, 56, 72]

(3)如果是两个列表中的元素个数不相等呢?

list_x = [1,2,3,4,5,6,7,8]       # 8个元素
list_y = [1,2,3,4,5,6]        # 6个元素

r = map(lambda x,y:x * x + y,list_x,list_y)  

print(list(r))

-->
[2, 6, 12, 20, 30, 42]       # 6个元素。取决于较小长度的列表的长度

(二)reduce

1.定义结构

上节map是位于全局命名空间的,所以直接使用即可。
但是,reduce,不是位于全局命名空间,所以,要先从函数库模块functools中导入。

from functools import reduce       #导入的是reduce函数   
reduce(func,sequence,initial=None)
   # func:函数,必须有两个参数
   # sequence:序列,即按顺序排列的数据,即字符串str、列表list、元组tuple。
   # initial=None:初始值,默认是0

可能是一个参数列表,但必须是两个参数。这是基于其自我连续计算的功能设定的。
2.例题:

求列表[1,2,3,4,5,6,7,8]中所有元素的和?

from functools import reduce        #不要忘记要先导入,否则报错reduce未定义
list_x = [1,2,3,4,5,6,7,8]
r = reduce(lambda x,y:x + y,list_x)      #奇怪:y从哪里来的?
print(r)

-->
36         #得到的是一个数值

(1)执行过程

  • 初始情况:
    不只是拿到1,而是拿到1、2,取前两个元素对应x、y进行相加
  • 第二次开始:
    将前两个元素的结果3作为x,再次传入lambda表达式中,此时的y是第三个元素
  • 以此类推

(2)执行过程演示

(((1+2)+3)+4)+5+...

有(1)和(2)可知:
其本质是在做连续计算,在连续调用lambda表达式。
关键点是:上一次的结果,将作为下一次的参数进行运算。直至遍历完。

3.误区

reduce都是做连续相加运算的?

错误。应该是reduce是做连续计算的,不止可以做连续相加,还可以做连续相乘等运算。具体运算方式,取决于lambda表达式是如何定义的。

4.思考题:
在应用场景中,旅行者变成了二维走路,求其坐标?

(x,y)
(0,0)


(1,3)    result = (1,3)
(2,-2)   reuslt = (3,1)
(-2,3)   result = (1,4)

本题的关键点:
每次调用函数时,需要保存上一次函数调用的结果。
from functools import reduce       
list_x = [(0,0),(1,3),(2,-2),(-2,3)]
r = reduce(lambda x,y:x + y,list_x)    
print(r)

-->
(0, 0, 1, 3, 2, -2, -2, 3)    ?

5.关于第三个参数initial

from functools import reduce       
list_x = ['1','2','3','4','5','6','7','8']
r = reduce(lambda x,y:x + y,list_x,'aaa')     # 'aaa'是初始值 
print(r)

-->
aaa12345678       #所以'aaa'出现在最前面,即第一次运算是:'aaa'+‘1’

拓展

谷歌提出了一个大数据计算模型:“map/reduce”.其中,map是映射,reduce是归约
最主要的应用:并行计算
借鉴思想:函数式编程

我的看法:
map就是在做循环计算,reduce就是在做自我连续计算。
共性是:都是在遍历。
区别是:(1)map得到是列表,reduce得到的是值。
(2)reduce上一次的结果,将作为下一次的参数进行运算。

(三)filter

顾名思义,就是将不符合我们规则的元素给过滤掉。
所以,其本质是条件判断。

即,常与lambda表达式、三元表达式配合使用。
所以,filter的特点,必须依托上面两位方能实现。

1.定义结构

filter(func,literable)        #filter是个类
    #func:结果必须是能够返回可以判断真假的结果。eg True,False;1,0;非空与空
    #见第3章 条件控制语句 if mood
    #见第1章 bool类型

2.例题

把列表中所有数值为0的元素,剔除掉?

list_x = [1,0,1,0,0,1]
r = filter(lambda x:True if x==1 else False,list_x)
print(r)
print(list(r))

-->
<filter object at 0x000001494BADAE30>     #跟map一样,得到的是对象
[1, 1, 1]

也可以写为

list_x = [1,0,1,0,0,1]
r = filter(lambda x:x,list_x)    
          #lambda表达式中的算式只用x即可,因为其结果要么是1,要么是0.刚好
print(list(r))

-->
[1, 1, 1

3.思考题

将列表中的小写的元素都剔除掉?

list_x = [A,a,B,b,C,c]
r= filter(lambda x:x if x in [A-Z] else None,list_x)
print(list(r))

-->
?

上述判断大小写,在数据处理方面非常有用。因为通常,我们是通过大写,来判断是否是句子开头。

四、函数式编程与命令式编程小结对比

1.命令式编程的关键点
其实就是我们每天都在写的过程控制。
无论多么复杂的业务逻辑,控制流程的关键点是:

  • 要有函数 def
  • 要有条件判断 if else
  • 要有循环 for

上述三个,是命令是编程最显著的特征。

2.对于函数式编程,常用的关键字有:

  • 要有lambda算子(函数式编程最基本的单元)---------代替函数 def
  • 要有filter表达式、三元表达式--------------------代替 if else
  • 要有map表达式、reduce表达式---------------------代替 for 循环

理论上,函数式编程可以一定程度上,代替命令式编程。但不建议,全盘用函数式编程,构建业务逻辑与项目.

因为

命令式语言函数式编程专门语言
Java、C#、Pythonlisp
只是对函数式编程有一定程度的支持,但并不是函数式编程的语言人工智能用的比较多
目前,是大家习惯

上述函数式编程的高阶函数,只是让代码更加简洁,不会提升运行效率。

声明:Jerry's Blog|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - 09 函数式编程之一


Stop chasing money, and start chasing the solutions to the problem.