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推導式所替代。