python學(xué)習(xí)-函數(shù)式編程-高階函數(shù)-map/reduce/filter/sorted

1.函數(shù)式編程


1.1 高階函數(shù)

函數(shù)是Python內(nèi)建支持的一種封裝,我們通過(guò)把大段代碼拆成函數(shù)健田,通過(guò)一層一層的函數(shù)調(diào)用烛卧,就可以把復(fù)雜任務(wù)分解成簡(jiǎn)單的任務(wù),這種分解可以稱(chēng)之為面向過(guò)程的程序設(shè)計(jì)妓局。函數(shù)就是面向過(guò)程的程序設(shè)計(jì)的基本單元总放。

而函數(shù)式編程(請(qǐng)注意多了一個(gè)“式”字)——Functional Programming,雖然也可以歸結(jié)到面向過(guò)程的程序設(shè)計(jì)好爬,但其思想更接近數(shù)學(xué)計(jì)算局雄。

我們首先要搞明白計(jì)算機(jī)(Computer)和計(jì)算(Compute)的概念。

在計(jì)算機(jī)的層次上抵拘,CPU執(zhí)行的是加減乘除的指令代碼哎榴,以及各種條件判斷和跳轉(zhuǎn)指令,所以僵蛛,匯編語(yǔ)言是最貼近計(jì)算機(jī)的語(yǔ)言尚蝌。

而計(jì)算則指數(shù)學(xué)意義上的計(jì)算,越是抽象的計(jì)算充尉,離計(jì)算機(jī)硬件越遠(yuǎn)飘言。

對(duì)應(yīng)到編程語(yǔ)言,就是越低級(jí)的語(yǔ)言驼侠,越貼近計(jì)算機(jī)姿鸿,抽象程度低,執(zhí)行效率高倒源,比如C語(yǔ)言苛预;越高級(jí)的語(yǔ)言,越貼近計(jì)算笋熬,抽象程度高热某,執(zhí)行效率低,比如Lisp語(yǔ)言胳螟。

函數(shù)式編程就是一種抽象程度很高的編程范式昔馋,純粹的函數(shù)式編程語(yǔ)言編寫(xiě)的函數(shù)沒(méi)有變量,因此糖耸,任意一個(gè)函數(shù)秘遏,只要輸入是確定的,輸出就是確定的嘉竟,這種純函數(shù)我們稱(chēng)之為沒(méi)有副作用邦危。而允許使用變量的程序設(shè)計(jì)語(yǔ)言洋侨,由于函數(shù)內(nèi)部的變量狀態(tài)不確定,同樣的輸入倦蚪,可能得到不同的輸出凰兑,因此,這種函數(shù)是有副作用的审丘。

函數(shù)式編程的一個(gè)特點(diǎn)就是,允許把函數(shù)本身作為參數(shù)傳入另一個(gè)函數(shù)勾给,還允許返回一個(gè)函數(shù)滩报!

Python對(duì)函數(shù)式編程提供部分支持。由于Python允許使用變量播急,因此脓钾,Python不是純函數(shù)式編程語(yǔ)言。

1.1 高階函數(shù)

高階函數(shù)英文叫Higher-order function桩警。
1.11 變量可以指向函數(shù)
以Python內(nèi)置的求絕對(duì)值的函數(shù)abs()為例可训,調(diào)用該函數(shù)用以下代碼:

>>> abs(-10)
10

但是,如果只寫(xiě)abs呢捶枢?

>>> abs
<built-in function abs>

可見(jiàn)握截,abs(-10)是函數(shù)調(diào)用,而abs是函數(shù)本身烂叔。
函數(shù)名也是變量

那么函數(shù)名是什么呢谨胞?函數(shù)名其實(shí)就是指向函數(shù)的變量!對(duì)于abs()這個(gè)函數(shù)蒜鸡,完全可以把函數(shù)名abs看成變量胯努,它指向一個(gè)可以計(jì)算絕對(duì)值的函數(shù)!
傳入函數(shù)

既然變量可以指向函數(shù)逢防,函數(shù)的參數(shù)能接收變量叶沛,那么一個(gè)函數(shù)就可以接收另一個(gè)函數(shù)作為參數(shù),這種函數(shù)就稱(chēng)之為高階函數(shù)忘朝。
一個(gè)最簡(jiǎn)單的高階函數(shù):

def add(x, y, f):
    return f(x) + f(y)

當(dāng)我們調(diào)用add(-5, 6, abs)時(shí)灰署,參數(shù)xyf分別接收-5辜伟,6和abs氓侧,根據(jù)函數(shù)定義,我們可以推導(dǎo)計(jì)算過(guò)程為:

x = -5
y = 6
f = abs
f(x) + f(y) ==> abs(-5) + abs(6) ==> 11
return 11

用代碼驗(yàn)證一下:

>>> add(-5, 6, abs)
11

編寫(xiě)高階函數(shù)导狡,就是讓函數(shù)的參數(shù)能夠接收別的函數(shù)约巷。

把函數(shù)作為參數(shù)傳入,這樣的函數(shù)稱(chēng)為高階函數(shù)旱捧,函數(shù)式編程就是指這種高度抽象的編程范式独郎。

1.11 map/reduce

Python內(nèi)建了map()reduce()函數(shù)踩麦。
我們先看map。map()函數(shù)接收兩個(gè)參數(shù)氓癌,一個(gè)是函數(shù)谓谦,一個(gè)是Iterablemap將傳入的函數(shù)依次作用到序列的每個(gè)元素贪婉,并把結(jié)果作為新的Iterator返回反粥。
舉例說(shuō)明,比如我們有一個(gè)函數(shù)f(x)=x2疲迂,要把這個(gè)函數(shù)作用在一個(gè)list [1, 2, 3, 4, 5, 6, 7, 8, 9]上才顿,就可以用map()實(shí)現(xiàn)如下:

>>> def f(x):
...     return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]

map()傳入的第一個(gè)參數(shù)是f,即函數(shù)對(duì)象本身尤蒿。由于結(jié)果r是一個(gè)Iterator郑气,Iterator是惰性序列,因此通過(guò)list()函數(shù)讓它把整個(gè)序列都計(jì)算出來(lái)并返回一個(gè)list腰池。

你可能會(huì)想尾组,不需要map()函數(shù),寫(xiě)一個(gè)循環(huán)示弓,也可以計(jì)算出結(jié)果:

L = []
for n in [1, 2, 3, 4, 5, 6, 7, 8, 9]:
    L.append(f(n))
print(L)

reduce把一個(gè)函數(shù)作用在一個(gè)序列[x1, x2, x3, ...]上讳侨,這個(gè)函數(shù)必須接收兩個(gè)參數(shù),reduce把結(jié)果繼續(xù)和序列的下一個(gè)元素做累積計(jì)算避乏,其效果就是:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

比方說(shuō)對(duì)一個(gè)序列求和爷耀,就可以用reduce實(shí)現(xiàn):

>>> from functools import reduce
>>> def add(x, y):
...     return x + y
...
>>> reduce(add, [1, 3, 5, 7, 9])
25

str轉(zhuǎn)換為int的函數(shù):

>>> from functools import reduce
>>> 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

整理成一個(gè)str2int的函數(shù)就是:

from functools import reduce

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函數(shù)進(jìn)一步簡(jiǎn)化成:

from functools import reduce

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))

也就是說(shuō),假設(shè)Python沒(méi)有提供int()函數(shù)拍皮,你完全可以自己寫(xiě)一個(gè)把字符串轉(zhuǎn)化為整數(shù)的函數(shù)歹叮,而且只需要幾行代碼!
lambda函數(shù)的用法在后面介紹铆帽。

1.12 filter

Python內(nèi)建的filter()函數(shù)用于過(guò)濾序列咆耿。
map()類(lèi)似,filter()也接收一個(gè)函數(shù)和一個(gè)序列爹橱。和map()不同的是萨螺,filter()把傳入的函數(shù)依次作用于每個(gè)元素,然后根據(jù)返回值是True還是False決定保留還是丟棄該元素愧驱。

例如慰技,在一個(gè)list中,刪掉偶數(shù)组砚,只保留奇數(shù)吻商,可以這么寫(xiě):

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

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

把一個(gè)序列中的空字符串刪掉,可以這么寫(xiě):

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

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

可見(jiàn)用filter()這個(gè)高階函數(shù)糟红,關(guān)鍵在于正確實(shí)現(xiàn)一個(gè)“篩選”函數(shù)艾帐。
注意到filter()函數(shù)返回的是一個(gè)Iterator乌叶,也就是一個(gè)惰性序列,所以要強(qiáng)迫filter()完成計(jì)算結(jié)果柒爸,需要用list()函數(shù)獲得所有結(jié)果并返回list准浴。

練習(xí):回?cái)?shù)是指從左向右讀和從右向左讀都是一樣的數(shù),例如12321捎稚,909乐横。請(qǐng)利用filter()濾掉非回?cái)?shù):

def is_palindrome(n):
    return str(n)[::1]==str(n)[::-1]
output = filter(is_palindrome, range(1,1000))
print(list(output))

1.1.3 sorted

排序也是在程序中經(jīng)常用到的算法。無(wú)論使用冒泡排序還是快速排序今野,排序的核心是比較兩個(gè)元素的大小晰奖。如果是數(shù)字,我們可以直接比較腥泥,但如果是字符串或者兩個(gè)dict呢?直接比較數(shù)學(xué)上的大小是沒(méi)有意義的啃匿,因此蛔外,比較的過(guò)程必須通過(guò)函數(shù)抽象出來(lái)。
sorted()函數(shù)也是一個(gè)高階函數(shù)溯乒,它還可以接收一個(gè)key函數(shù)來(lái)實(shí)現(xiàn)自定義的排序夹厌,例如按絕對(duì)值大小排序:

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

練習(xí)

假設(shè)我們用一組tuple表示學(xué)生名字和成績(jī):

L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]·
請(qǐng)用sorted()對(duì)上述列表分別按名字排序:

L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
def by_name(t):
    return t[0]
def by_score(t):
    return t[1]
L2=sorted(L,key=by_name)
L3=sorted(L,key=by_score,reverse=True)
print (L2)
print (L3)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市裆悄,隨后出現(xiàn)的幾起案子矛纹,更是在濱河造成了極大的恐慌,老刑警劉巖光稼,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件或南,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡艾君,警方通過(guò)查閱死者的電腦和手機(jī)采够,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)冰垄,“玉大人蹬癌,你說(shuō)我怎么就攤上這事『绮瑁” “怎么了逝薪?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蝴罪。 經(jīng)常有香客問(wèn)我董济,道長(zhǎng),這世上最難降的妖魔是什么洲炊? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任感局,我火速辦了婚禮尼啡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘询微。我一直安慰自己崖瞭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布撑毛。 她就那樣靜靜地躺著书聚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪藻雌。 梳的紋絲不亂的頭發(fā)上雌续,一...
    開(kāi)封第一講書(shū)人閱讀 49,071評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音胯杭,去河邊找鬼驯杜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛做个,可吹牛的內(nèi)容都是我干的鸽心。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼居暖,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼顽频!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起太闺,我...
    開(kāi)封第一講書(shū)人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤糯景,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后省骂,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蟀淮,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年钞澳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了灭贷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡略贮,死狀恐怖甚疟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情逃延,我是刑警寧澤览妖,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站揽祥,受9級(jí)特大地震影響讽膏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拄丰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一府树、第九天 我趴在偏房一處隱蔽的房頂上張望俐末。 院中可真熱鬧,春花似錦奄侠、人聲如沸卓箫。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)烹卒。三九已至,卻和暖如春弯洗,著一層夾襖步出監(jiān)牢的瞬間旅急,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工牡整, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留藐吮,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓逃贝,卻偏偏與公主長(zhǎng)得像炎码,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子秋泳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容