参考原文
什么是高阶函数(Higher-order function)?
变量可以指向函数
>>> f = abs >>> f <built-in function abs> >>> f(-10) 10
函数名也是变量
函数名是什么呢?函数名其实就是指向函数的变量。对于abs()这个函数,完全可以这样看:函数名abs是一个变量,它指向一个可以计算绝对值的函数。如果把abs指向整型数据类型,再来调用会发生什么呢?
>>> abs = 10 >>> abs(-10) Traceback (most recent call last): File "<pyshell#92>", line 1, in <module> abs(-10) TypeError: 'int' object is not callable
因为abs这个变量已经不指向求绝对值的函数而是指向一个整数10了!
传入函数
既然变量可以指向函数,函数的参数能接受变量,那么一个函数就可以接受另一个函数作为参数,这种能接受函数作为参数的函数,就叫高阶函数。一个简单的高阶函数:
# -*- conding:utf-8 -*- def add(x, y, f): return f(x) + f(y) print(add(-5, 6, abs)) # result 11
Tips:把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。
map
map函数接受2个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到Iterable序列的每个元素,并把结果作为Iterator返回。
问题:已知函数f(x)=x 2,使将该函数作用在一个list[1, 2, 3, 4, 5, 6, 7, 8, 9]上。
在之前,你可能会用循环这么做:
def f(x): return x * x L = [] for n in [1, 2, 3, 4, 5, 6, 7, 8, 9]: L.append(f(n)) print(L) #result [1, 4, 9, 16, 25, 36, 49, 64, 81]
但是从上面的循环代码来看,不太能一眼看明白"把f(x)作用在list的每一个元素上并把结果生成一个新的list"。现在,我们应该用map试一试:
def f(x): return x * x r = map(f,[1, 2, 3, 4, 5, 6, 7, 8, 9]) print(list(r)) #result [1, 4, 9, 16, 25, 36, 49, 64, 81]
这样似乎是要简单点的,map()传入的第一个参数时f,即函数对象本身。由于得到的结果r是一个Iterator惰性序列,因此通过list()函数让它把整个序列计算出来并返回出一个list对象。
map()作为高阶函数,它把运算规则抽象了,我们可以计算任意复杂的函数,比如把这个list所有数字转为字符串:
>>> list(map(str, [1, 2, 3, 4])) ['1', '2', '3', '4']
reduce
reduce函数也接受2个参数,一个是函数,一个是Iterable,和map不同的是,这个函数必须接受2个参数,reduce强调把结果继续和序列的下一个元素做累积计算,返回累积后具体的数据,其效果如下:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
可以用于对一个序列求和:
from functools import reduce def add(x, y): return x + y print(reduce(add, [1, 3, 5, 7, 9])) #result 25
如果要把序列[1, 3, 5, 7, 9]变成整数13579,那么reduce就可以派上用场了:
from functools import reduce def fn(x, y): return x * 10 + y print(reduce(fn, [1, 3, 5, 7, 9])) #resuolt 13579
考虑到字符串str也是一个序列,对上面的例子稍加改动,配合map(),我们可以写成把str转换成int的函数:
from functools import reduce DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9} def str2int(s): def fn(x, y): return x * 10 + y def char2num(s): return DIGITS[s] return reduce(fn, map(char2num, s)) print(str2int('12345')) #result 整数12345
filter
Python内建的filter()函数用于过滤序列,和map()类似,filter()也接受一个函数和一个序列,和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素,返回的是一个Iterator。
例如,在一个list中,删掉偶数,只保留奇数:
>>> list(filter(lambda x: x % 2 == 1, [1, 2, 4, 5, 6])) [1, 5]
把一个序列中的空字符串删除:
>>> list(filter(lambda s: s and s.strip(), ['A', '', 'B', None, 'C', ' '])) ['A', 'B', 'C']
Tips:正确地使用filter函数,关键在于正确地实现一个"筛选"函数。由于filter使用了惰性序列计算,所以只有在取filter()结果的时候,才会真正筛选并返回下一个筛选出的元素哦!
sorted
排序在程序中也是经常用到的算法,无论是冒泡排序还是快速排序,排序的核心是比较两个元素的大小。如果是数字,我们可以直接比较,但如果是字符串或是2个dict呢?直接比较数学上的大小是没有意义的,因此比较的过程必须通过函数抽象出来。
为此Python内置了sorted()函数,它可以对list进行排序:
>>> sorted([3, 4, 1, 2, -1])
[-1, 1, 2, 3, 4]
但同时,sorted()也是一个高阶函数,它还可以接受一个key函数来实现自定义的排序,如按绝对值大小的排序:
>>> sorted([3, 4, 5, 2, -1], key=abs)
[-1, 2, 3, 4, 5]
key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序。
Tips:soretd()也是一个高阶函数,用sorted()进行排序的关键在于实现一个映射函数。