Map,Filter 和 Reduce 三个高阶函数能为函数式编程提供便利。
首先看一下什么是MapReduce?
摘自wiki中关于MapReduce的解释:
MapReduce是Google提出的一个软件架构,用于大规模数据集(大于1TB)的并行运算。概念“Map(映射)”和“Reduce(归纳)”,及他们的主要思想,都是从函数式编程语言借来的,还有从矢量编程语言借来的特性。
当前的软件实现是指定一个Map(映射)函数,用来把一组键值对映射成一组新的键值对,指定并发的Reduce(归纳)函数,用来保证所有映射的键值对中的每一个共享相同的键组。
简单来说,一个映射函数就是对一些独立元素组成的概念上的列表(例如,一个测试成绩的列表)的每一个元素进行指定的操作(比如,有人发现所有学生的成绩都被高估了一分,他可以定义一个“减一”的映射函数,用来修正这个错误)。事实上,每个元素都是被独立操作的,而原始列表没有被更改,因为这里创建了一个新的列表来保存新的答案。这就是说,Map操作是可以高度并行的,这对高性能要求的应用以及并行计算领域的需求非常有用。 而归纳操作指的是对一个列表的元素进行适当的合并(继续看前面的例子,如果有人想知道班级的平均分该怎么做?他可以定义一个归纳函数,通过让列表中的奇数(odd)或偶数(even)元素跟自己的相邻的元素相加的方式把列表减半,如此递归运算直到列表只剩下一个元素,然后用这个元素除以人数,就得到了平均分)。虽然他不如映射函数那么并行,但是因为归纳总是有一个简单的答案,大规模的运算相对独立,所以归纳函数在高度并行环境下也很有用。
一、Map
1.1 定义及基础用法
Map会将一个函数映射到一个输入序列的所有元素上。Map() 会根据提供的函数对指定序列做映射。
第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。
func是None的情况,它的目的是将多个列表相同位置的元素归并到一个元组,在现在已经有了专用的函数zip()了。
不同长度的多个seq是无法执行map函数的,会出现类型错误。
基础语法:
map(function_to_apply, list_of_inputs)
map(*function*, *iterable*, *...*) -> list
参数:
function_to_apply:映射函数,也就是处理输入迭代类型的的每一个元素的函数。
list_of_inputs:一个或多个序列。输入的序列类型:列表,集合,元组,字典及字符串。
返回值:
Python2 :map直接返回列表,不管输入的迭代类型是列表,集合,元组,字典还是字符串类型。
Python3:map返回迭代器,可以利用list()转为列表,只能转为列表,set(), tuple()等函数不起作用。
Iterator是惰性序列,因此通过list函数让它把整个序列都计算出来并返回一个list。
示例:
Python 2:
a = [1, 2, 3, 4]
b = map(lambda x: x*2, a) # 处理列表
Out[4]: [2, 4, 6, 8]
b = map(lambda x: x*2, (1,2,3)) # 处理元组
Out[6]: [2, 4, 6]
b = map(lambda x: x*2, {1:2, 3:4, 5:6}) # 处理字典,只处理键
Out[8]: [2, 6, 10]
b = map(lambda x: x*2, {1:2, 3:4, 5:6}.keys())
Out[17]: [2, 6, 10]
b = map(lambda x: x*2, {1:2, 3:4, 5:6}.values())
Out[15]: [4, 8, 12]
b = map(lambda x: x*2, set([1, 2, 3, 4])) # 处理集合
Out[11]: [2, 4, 6, 8]
b = map(lambda x: x+"1", "12345") # 处理字符串,字符串也是迭代类型
Out[19]: ['11', '21', '31', '41', '51']
Python 3
>>> a = [1, 2, 3, 4]
>>> b = map(lambda x: x*2, a) # 处理列表
>>> b
<map object at 0x0000000002B68A20> # map对象
>>> list(b)
[2, 4, 6, 8]
1.2 高级用法
1.2.1 处理多个序列
想要输入和处理多个序列,需要可以支持多个参数的函数,注意的是各序列的长度必须一样,否则报错。
# 提供了两个列表,对相同位置的列表数据进行相加
>>> map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
[3, 7, 11, 15, 19]
>>> map(add,'zhoujy','Python','test') #'test'的长度比其他2个小
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: add() takes exactly 2 arguments (3 given)
单个序列:seq中的每个元素都经过了func函数的作用,得到了func(seq[n])组成的列表。
多个序列:每个seq的同一位置的元素在执行过一个多元的func函数之后,得到一个返回值,这些返回值放在一个结果列表中。
print map( lambda x, y: x * y, [1, 2, 3], [4, 5, 6] ) # [4, 10, 18]
print map( lambda x, y: ( x * y, x + y), [1, 2, 3], [4, 5, 6] ) # [(4, 5), (10, 7), (18, 9)]
返回值是一个值,也可以是一个元组。
1.2.2 处理函数序列
大多数时候,我们使用匿名函数(lambdas)来配合map, 不仅可以用于一序列的输入, 也可以用于一列表的函数。
def multiply(x):
return (x*x)
def add(x):
return (x+x)
funcs = [multiply, add]
for i in range(5):
value = map(lambda x: x(i), funcs)
print(list(value))
# 译者注:上面print时,加了list转换,是为了python2/3的兼容性
# 在python2中map直接返回列表,但在python3中返回迭代器
# 因此为了兼容python3, 需要list转换一下
# Output:
# [0, 0]
# [1, 2]
# [4, 4]
# [9, 6]
# [16, 8]
二、Reduce
2.1 定义及基础用法
reduce() 函数会对参数序列中元素进行累积。
函数将一个数据集合(列表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果,逐步迭代。
也就是reduce函数把前两个元素的计算结果继续和序列的下一个元素做累积计算,直到处理完所有元素,返回结果。以下两个函数等价:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
基本语法:
reduce(function, iterable[, initializer])
reduce(*function*, *iterable*[, *initializer*]) -> value
参数:
- function -- 函数,有两个参数
- iterable -- 可迭代对象
- initializer -- 可选,初始参数。第一次时为init的元素,如没有init则为seq的第一个元素。
返回值:
返回函数计算结果。
示例:
>>>def add(x, y) : # 两数相加
... return x + y
...
>>> reduce(add, [1,2,3,4,5]) # 计算列表和:1+2+3+4+5
15
>>> reduce(lambda x, y: x+y, [1,2,3,4,5]) # 使用 lambda 匿名函数
15
上述执行过程是:1+2=3,3+3=6,6+4=10,10+5=15。
2.2 高级用法
2.2.1 处理函数序列
使用匿名函数(lambdas)来配合reduce, 不仅可以用于一序列的输入, 也可以用于一个函数序列,但是要注意lambda表达式的参数是两个。
def multiply(x):
return (x * x)
def add(x):
return (x + x)
funcs = [multiply, add]
for i in range(5):
value = reduce(lambda x, y: x(i) + y(i), funcs)
print(value)
0
3
8
15
24
三、Filter
3.1 定义及基础用法
Python 2:
filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。
Python 3:
filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回一个迭代器对象,如果要转换为列表,可以使用 list() 来转换,利用set转换为集合,tuple转换为元组,str转为字符串。
另外注:集合是{},列表是[],元组是(),字典是{k1:v1, k2:v2}。
注意: Pyhton2.7 返回列表,Python3.x 返回迭代器对象。
Filter是一个内置函数,并且比for循环更快。
基本语法:
filter(function, iterable)
filter(function or None, sequence) -> list, tuple, or string
参数:
其接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判断,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。
- function -- 判断函数。过滤序列中每个元素的函数。
- iterable -- 可迭代对象。需要过滤的序列。
返回值:
Python 2:返回序列,不只是列表,也可能是元组,字符串。
列表,集合,字典返回列表,元组返回元组,字符串返回字符串。字典处理键。
Python 3:返回一个迭代器对象,filter object(filter对象)。
Python3 中返回到是一个 filter 类对象。filter 类实现了 __iter__ 和 __next__ 方法, 可以看成是一个迭代器, 有惰性运算的特性(类似于生成器), 相对 Python2.x 提升了性能, 可以节约内存。
a = filter(lambda x: x % 2 == 0, range(10))
print(a)
输出:
<filter object at 0x0000022EC66BB128>
示例:
Python 2:
过滤出列表中的所有奇数:
def is_odd(n):
return n % 2 == 1
newlist = filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print(newlist)
[1, 3, 5, 7, 9]
Python 3:
def is_odd(n):
return n % 2 == 1
tmplist = filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
newlist = list(tmplist)
print(newlist)
[1, 3, 5, 7, 9]
四、总结:
(1)map函数和map函数与MapReduce的机制原理相同。map函数对序列的每一个元素进行处理,而resuce函数则两两迭代逐步计算。
(2)reduce函数和filter函数只能处理一个序列。
(3)注意在 python2 和 python3 中,map/reduce/filter 的返回值类型有所不同,python2 返回的是基本数据类型,而 python3 则返回了迭代器。
(4)filter函数很容易被for循环和for in if推导式所替代。