what's the 高阶函数
把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。
python内置的高阶函数主要有map、reduce、filter、sorted,当然我们可以自己编写高阶函数
Map函数
map()函数接收两个参数,一个是函数,一个是序列,map将传入的函数依次作用到序列的每个元素,并把结果作为新的对象返回,返回值是一个可迭代对象,可以用list()方法将其转为一个列表。
举例说明
比如我们有一个函数f(x)=x2,要把这个函数作用在一个list [1, 2, 3, 4, 5, 6, 7, 8, 9]上,就可以用map()实现如下:
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)# [1, 4, 9, 16, 25, 36, 49, 64, 81]
# 用map实现
list(map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])) # [1, 4, 9, 16, 25, 36, 49, 64, 81]
# 用map将列表重的数字转换为字符串
list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])) # ['1', '2', '3', '4', '5', '6', '7', '8', '9']
Reduce函数
reduce把一个函数作用在一个序列[x1, x2, x3…]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:
#reduce函数不是内置函数,而是在模块functools中的函数,故需要导入
from functools import 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
reduce(add, [1, 3, 5, 7, 9]) # 25
当然求和运算可以直接用Python内建函数sum(),没必要动用reduce。 但是如果要把序列 [1, 3, 5, 7, 9] 变换成整数13579,reduce就可以派上用场:
from functools import reduce
def fn(x, y):
return x * 10 + y
reduce(fn, [1, 3, 5, 7, 9]) # 13579
这个例子本身没多大用处,但是,如果考虑到字符串str也是一个序列,对上面的例子稍加改动,配合map(),我们就可以写出把str转换为int的函数:
def fn(x, y):
return x * 10 + y
def char2num(s):
return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
reduce(fn, map(char2num, '13579')) # 13579
# 整理成一个str2int的函数就是:
def str2int(s):
def fn(x, y):
return x * 10 + y
def char2num(s):
return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
return reduce(fn, map(char2num, s))
上面的例子还可以用lambda函数进一步简化成:
def char2num(s):
return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
def str2int(s):
return reduce(lambda x,y: x*10+y, map(char2num, s))
也就是说,假设Python没有提供int()函数,完全可以自己写一个把字符串转化为整数的函数,而且只需要几行代码!
Filter函数
Python内建的filter()函数用于过滤序列。和map()类似,filter()也接收一个函数和一个序列。和map()不同的时,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。True保留,False丢弃
例如,在一个list中,删掉偶数,只保留奇数,可以这么写:
def is_odd(n):
return n % 2 == 1
filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])# [1, 5, 9, 15]
把一个序列中的空字符串删掉,可以这么写:
def not_empty(s):
return s and s.strip()
filter(not_empty, ['A', '', 'B', None, 'C', ' ']) # ['A', 'B', 'C']
所以用filter()关键在于正确实现一个“筛选”函数。
sorted函数
排序也是在程序中经常用到的算法。无论使用冒泡排序还是快速排序,排序的核心是比较两个元素的大小。如果是数字,我们可以直接比较,但如果是字符串或者两个dict呢?直接比较数学上的大小是没有意义的,因此,比较的过程必须通过函数抽象出来。通常规定,对于两个元素x和y,如果认为x < y,则返回-1,如果认为x == y,则返回0,如果认为x > y,则返回1,这样,排序算法就不用关心具体的比较过程,而是根据比较结果直接排序。
Python内置的sorted()函数就可以对list进行排序:
sorted([36, 5, 12, 9, 21]) # [5, 9, 12, 21, 36]
此外,sorted()函数也是一个高阶函数,它还可以接收一个比较函数来实现自定义的排序。比如,如果要倒序排序,我们就可以自定义一个reversed_cmp函数:
def reversed_cmp(x, y):
if x > y:
return -1
if x < y:
return 1
return 0
# 传入自定义的比较函数reversed_cmp,就可以实现倒序排序:
sorted([36, 5, 12, 9, 21], reversed_cmp) # [36, 21, 12, 9, 5]
再看一个字符串排序的例子:
sorted(['bob', 'about', 'Zoo', 'Credit']) # ['Credit', 'Zoo', 'about', 'bob']
默认情况下,对字符串排序,是按照ASCII的大小比较的,由于'Z' < 'a',结果,大写字母Z会排在小写字母a的前面。
现在,我们提出排序应该忽略大小写,按照字母序排序。要实现这个算法,不必对现有代码大加改动,只要我们能定义出忽略大小写的比较算法就可以:
def cmp_ignore_case(s1, s2):
u1 = s1.upper()
u2 = s2.upper()
if u1 < u2:
return -1
if u1 > u2:
return 1
return 0
sorted(['bob', 'about', 'Zoo', 'Credit'], cmp_ignore_case) # ['about', 'bob', 'Credit', 'Zoo']