1. 程式人生 > >Python(八)語法 函數語言程式設計(map/reduce/filter/sorted)

Python(八)語法 函數語言程式設計(map/reduce/filter/sorted)

map

map()函式接收兩個引數,一個是函式,一個是Iterablemap將傳入的函式依次作用到序列的每個元素,並把結果作為新的Iterator返回。,簡單點說就是讓每個元素執行函式,如下對L中每個元素求平方

>>> def f(x):
...  return x*x
...
>>> L=[1,2,3,4,5]
>>> r=map(f,L)
>>> r
<map object at 0x00000000029105F8>
>>> list(r)
[1, 4, 9, 16, 25]
>>>

map()傳入的第一個引數是f,即函式物件本身。由於結果r是一個IteratorIterator是惰性序列,因此通過list()函式讓它把整個序列都計算出來並返回一個list。

map()作為高階函式,事實上它把運算規則抽象了,因此,我們不但可以計算簡單的f(x)=x2,還可以計算任意複雜的函式,比如,把這個list所有數字轉為字串

>>> list(map(str,L))
['1', '2', '3', '4', '5']

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
...
>>> reduce(add, [1, 3, 5, 7, 9])
25

filter

Python內建的filter()函式用於過濾序列。

map()類似,filter()也接收一個函式和一個序列。和map()

不同的是,filter()把傳入的函式依次作用於每個元素,然後根據返回值是True還是False決定保留還是丟棄該元素。

例如,在一個list中,刪掉偶數,只保留奇數,可以這麼寫:

def is_odd(n):
    return n % 2 == 1

list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 結果: [1, 5, 9, 15]

把一個序列中的空字串刪掉,可以這麼寫:

def not_empty(s):
    return s and s.strip()

list(filter(not_empty, ['A', '', 'B', None, 'C', '  ']))
# 結果: ['A', 'B', 'C']

可見用filter()這個高階函式,關鍵在於正確實現一個“篩選”函式。

注意到filter()函式返回的是一個Iterator,也就是一個惰性序列,所以要強迫filter()完成計算結果,需要用list()函式獲得所有結果並返回list。

排序演算法

排序也是在程式中經常用到的演算法。無論使用氣泡排序還是快速排序,排序的核心是比較兩個元素的大小。如果是數字,我們可以直接比較,但如果是字串或者兩個dict呢?直接比較數學上的大小是沒有意義的,因此,比較的過程必須通過函式抽象出來。

Python內建的sorted()函式就可以對list進行排序:

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

此外,sorted()函式也是一個高階函式,它還可以接收一個key函式來實現自定義的排序,例如按絕對值大小排序:

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

key指定的函式將作用於list的每一個元素上,並根據key函式返回的結果進行排序。對比原始的list和經過key=abs處理過的list:

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

keys = [36, 5,  12, 9,  21]

然後sorted()函式按照keys進行排序,並按照對應關係返回list相應的元素:

keys排序結果 => [5, 9,  12,  21, 36]
                |  |    |    |   |
最終結果     => [5, 9, -12, -21, 36]

我們再看一個字串排序的例子:

>>> sorted(['bob', 'about', 'Zoo', 'Credit'])
['Credit', 'Zoo', 'about', 'bob']

預設情況下,對字串排序,是按照ASCII的大小比較的,由於'Z' < 'a',結果,大寫字母Z會排在小寫字母a的前面。

現在,我們提出排序應該忽略大小寫,按照字母序排序。要實現這個演算法,不必對現有程式碼大加改動,只要我們能用一個key函式把字串對映為忽略大小寫排序即可。忽略大小寫來比較兩個字串,實際上就是先把字串都變成大寫(或者都變成小寫),再比較。

這樣,我們給sorted傳入key函式,即可實現忽略大小寫的排序:

>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']

要進行反向排序,不必改動key函式,可以傳入第三個引數reverse=True

>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']

從上述例子可以看出,高階函式的抽象能力是非常強大的,而且,核心程式碼可以保持得非常簡潔。

假設我們用一組tuple表示學生名字和成績:

L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]

按名字排序

# -*- coding: utf-8 -*-

L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
def by_name(t):
    return t[0]
L2 = sorted(L, key=by_name)
print(L2)
[('Adam', 92), ('Bart', 66), ('Bob', 75), ('Lisa', 88)]