什么是高阶函数?把函数作为参数传入或把函数做为结果值返回,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。函数式编程的特点:

  • 函数本身可以赋值给变量,赋值后变量为函数;
  • 允许将函数本身作为参数传入另一个函数;
  • 允许返回一个函数。

在前面的章节中,我们知道可以用abs()这个函数来得到一个数的绝对值,如:

print('abs(-100):', abs(-100))

以上代码,输出:

abs(-100): 100

如果,我们把代码修改下,把abs赋值给一个变量:

f = abs
print(f(-100))

以上代码,输出:

100

可见,abs(-100)是函数调用,而abs是函数本身,要获得函数调用结果,我们可以把结果赋值给变量。函数本身也可以赋值给变量,即:变量可以指向函数,这时变量就获得了函数的功能,如上例中的 f ,直接调用f()和直接调用abs()完全相同。

那么函数名是什么呢?函数名其实就是指向函数的变量!对于abs()这个函数,完全可以把函数名abs看成变量,它指向一个可以计算绝对值的函数!

如果把abs指向其他对象,会有什么情况发生?

abs = 10
print(abs(-10))

把abs指向10后,就无法通过abs(-10)调用该函数了!因为abs这个变量已经不指向求绝对值函数而是指向一个整数10!当然实际代码绝对不能这么写,这里是为了说明函数名也是变量。

注:由于abs函数实际上是定义在import builtins模块中的,所以要让修改abs变量的指向在其它模块也生效,要用import builtins; builtins.abs = 10。关于什么是模块,后面会讲到,这里不要去纠结。

既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。

一个最简单的高阶函数:

def add(x, y, f):
return f(x) + f(y)

当我们调用add(-5, 6, abs)时,参数x,y和f分别接收-5,6和abs,根据函数定义,我们可以推导计算过程为:

x = -5
y = 6
f = abs
f(x) + f(y) ==> abs(-5) + abs(6) ==> 11
return 11

用代码验证下:

def add(x, y, f):
return f(x) + f(y) print(add(-5, 6, abs))

1、map函数

这是Python内置的一个函数,map()函数接收两个参数,一个是函数,一个是Iterable(迭代器对象),map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator(迭代器)返回:

def f(x):
"""
返回一个数的3次方
:param x: 数
:return: 3次方后的结果
"""
return x**3 r = map(f, [1, 2, 3, 4, 5]) print(list(r))

以上代码,输出:

[1, 8, 27, 64, 125]

map()传入的第一个参数是f,即函数对象本身。由于结果r是一个Iterator,Iterator是惰性序列,因此通过list()函数让它把整个序列都计算出来并返回一个list。

当然,不需要map()函数,写一个循环,也可以计算出结果:

def f(x):
"""
返回一个数的3次方
:param x: 数
:return: 3次方后的结果
"""
return x**3 L = []
for n in [1, 2, 3, 4, 5]:
L.append(f(n)) print(L)

执行后,结果是一样的,但这样的写法,我们并不能直观的知道f()是作用在列表的所有元素上,并返回一个列表,我们必须读了源代码后才知道。

所以,map()作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以计算简单的f(x)=x3,还可以计算任意复杂的函数,比如,把这个list所有数字转为字符串:

print(list(map(str, [1, 2, 3, 4, 5])))

以上代码,输出:

['', '', '', '', '']

当然,更多的时候我们也会配合lambda来使用,如:

r = map(lambda x: x**3, [1, 2, 3, 4, 5])

print(list(r))

以上代码,输出:

[1, 8, 27, 64, 125]

2、reduce函数

reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

对一个序列求和,就可以用reduce实现:

from functools import reduce

def add(x, y):
return x + y print('reduce(add, [1, 3, 5, 7, 9]) = ', reduce(add, [1, 3, 5, 7, 9]))

以上代码,输出:

reduce(add, [1, 3, 5, 7, 9]) =  25

当然求和运算可以直接用Python内建函数sum(),思考:如何把序列 [1, 2, 3, 4, 5] 转化为整型的 12345 ,下面给出代码但请尽可能自己先写出

from functools import reduce

def f(x, y):
return x*10 + y res = reduce(f, [1, 2, 3, 4, 5]) print(res)

思考:假如python没有提供 int() 函数,如何使用 map与reduce自己写一个函数,实现将 '123456' 转化为 ,下面给出代码但请尽可能自己先写出:

from functools import reduce

def str2int(s):
"""
将字符串转为数字
:param s: 要转化的字符串
:return: 数字
""" DIGITS = {'': 0, '': 1, '': 2, '': 3, '': 4, '': 5, '': 6, '': 7, '': 8, '': 9}
def char2num(c):
"""
字符转数字
:param c:字符
:return: 数字
"""
return DIGITS[c] def f(x, y):
return x*10 + y resList = map(char2num, s) # 将字符串转为列表 return reduce(f, resList) # 将列表元素依次排列组成数字,并返回 print(str2int(''))

3、filter函数

Python内建的filter()函数用于过滤序列。filter()接收一个函数和一个序列,把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

例如,在一个list中,删掉偶数,只保留奇数,可以这么写:

def is_odd(n):
return n % 2 == 1 res = list(filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8]))
print(res)

以上代码,输出:

[1, 3, 5, 7]

注:filter()函数返回的是一个Iterator,也就是一个惰性序列,filter()的作用是从一个序列中筛出符合条件的元素。由于filter()使用了惰性计算,所以只有在取filter()结果的时候,才会真正筛选并每次返回下一个筛出的元素。

4、sorted函数

排序也是在程序中经常用到的算法。无论使用冒泡排序还是快速排序,排序的核心是比较两个元素的大小。如果是数字,我们可以直接比较,但如果是字符串或者两个dict呢?直接比较数学上的大小是没有意义的,因此,比较的过程必须通过函数抽象出来。

Python内置的sorted()函数就可以对list进行排序:

print(sorted([36, 5, -12, 9, -21]))

以上代码,输出:

[-21, -12, 5, 9, 36]

它还可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序:

print(sorted([36, 5, -12, 9, -21], key=abs))

以上代码,输出:

[5, 9, -12, -21, 36]

key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序。

这个是数字,那字符串排序是怎么样的呢?我们来看实例:

print(sorted(['hello', 'world', 'roy', 'python', 'c++']))

以上代码,输出:

['c++', 'hello', 'python', 'roy', 'world']

默认情况下,对字符串排序,是按照ASCII的大小比较的,每个字符依次比较(两个字符串比较,先比较第一个字符,当第一个字符分出大小时则结束比较且比较的结果即为第一个字符相比较的结果,当第一个字符相同时则比较第二个字符,第二个字符相同时则比较第三个,依次下去直到得出结果),由于'Z' < 'a',结果,大写字母Z会排在小写字母a的前面。

可以用 reverse=True 进行反向排序:

print(sorted([36, 5, -12, 9, -21], reverse=True))
print(sorted(['hello', 'world', 'roy', 'python', 'c++'], reverse=True))

以上代码,输出:

[36, 9, 5, -12, -21]
['world', 'roy', 'python', 'hello', 'c++']

python3 第二十章 - 函数式编程之Higher-order function(高阶函数)的更多相关文章

  1. python3 第二十二章 - 函数式编程之Decorator(装饰器)

    前面我们说了,在python中,一切皆对象.函数也是一个对象,而且函数对象可以被赋值给变量,通过变量也能调用该函数.如: def sayHello(name): print(name + ' hell ...

  2. Python函数式编程之map()

    Python函数式编程之map() Python中map().filter().reduce()这三个都是应用于序列的内置函数. 格式: map(func, seq1[, seq2,…]) 第一个参数 ...

  3. 函数式编程之 Python

    上接 python 函数式编程学习笔记 参考:www.sigai.cn/ 1 函数式编程概述 前提:函数在 Python 中是⼀等对象 工具:built-in ⾼阶函数:lambda 函数:opera ...

  4. Ramda函数式编程之PHP

    0x00 何为函数式编程 网上已经有好多详细的接受了,我认为比较重要的有: 函数是"第一等公民",即函数和其它数据类型一样处于平等地位 使用"表达式"(指一个单 ...

  5. java函数式编程之lambda表达式

    作为比较老牌的面向对象的编程语言java,在对函数式编程的支持上一直不温不火. 认为面向对象式编程就应该纯粹的面向对象,于是经常看到这样的写法:如果你想写一个方法,那么就必须把它放到一个类里面,然后n ...

  6. Python3基础(3)集合、文件操作、字符转编码、函数、全局/局部变量、递归、函数式编程、高阶函数

    ---------------个人学习笔记--------------- ----------------本文作者吴疆-------------- ------点击此处链接至博客园原文------ 1 ...

  7. [Python3] 035 函数式编程 高阶函数

    目录 函数式编程 之 高阶函数 1. 引子 2. 系统提供的高阶函数 3. functools 包提供的 reduce 4. 排序 函数式编程 之 高阶函数 把函数作为参数使用的函数,叫高阶函数 1. ...

  8. Python3学习之路~3.2 递归、函数式编程、高阶函数、匿名函数、嵌套函数

    1 递归 在函数内部,可以调用其他函数.如果一个函数在内部调用自身本身,这个函数就是递归函数. def calc(n): print(n) if int(n / 2) == 0: return n r ...

  9. 高阶函数 - Higher Order Function

    一个函数如果有 参数是函数 或 返回值是函数,就称为高阶函数. 这篇文章介绍高阶函数的一个子集:输入 fn,输出 fn'. 按 fn 与 fn' 功能是否一致,即相同输入是否始终对应相同输出,把这类高 ...

随机推荐

  1. php require、require_once和include、include_once的区别

    一.引入php文件路径的方法require '文件路径'; require ('文件路径');require_once '文件路径'; require_once ('文件路径');include 同 ...

  2. sql基本知识

    1.类型转换 用convert,cast float转换出现科学计数字母的问题:可以先转成numeric再转成varchar declare @fl float set @fl=1234567.123 ...

  3. 教你用.Net来玩微信跳一跳

    目前开发的所有代码都已经上传到了GitHub.欢迎大家来Star https://github.com/GiantLiu/AutoJump 目前程序分为“全自动版本”和“半自动版本” 全自动版本 We ...

  4. JS实现EasyUI ,Datagrid,合并单元格功能

    为了实现datagrid的合并单元格效果,datagrid的数据加载方式肯定是要写在JS文件内部的. 一:在JS内部添加Datagrid数据加载方法如下: $("#id").dat ...

  5. git常用使用命令

    Git远程操作详解(from 阮一峰) git clone.git remote.git fetch.git pull.git push git拉取远程分支并创建本地分支 git中文学习文档(详细) ...

  6. win10 uwp 横向 AppBarButton

    一般看到的 AppBarButton 都是图片在上面,文字在下面,是否可以更改让文字在和图片相同的位置?本文告诉大家如何做出横向的 AppBarButton 把图标和文本放在一起. 如果需要添加 Ap ...

  7. sprintf的用法

    正文:printf 可能是许多程序员在开始学习C 语言时接触到的第二个函数(我猜第一个是main),说起来,自然是老朋友了,可是,你对这个老朋友了解多吗?你对它的那个孪生兄弟sprintf 了解多吗? ...

  8. js 实现复制剪切

    原生js实现复制 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> < ...

  9. TLB和MMU的区别

    MMU是Memory Management Unit的缩写,中文名是内存管理单元,它是中央处理器(CPU)中用来管理虚拟存储器.物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制 ...

  10. 初窥Flask

    初窥Flask Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求 ...