函數(shù)式編程--高階函數(shù)

高階函數(shù)英文叫Higher-order function副渴。什么是高階函數(shù)奈附?舉例說明。
變量可以指向函數(shù)
以Python內(nèi)置的求絕對值的函數(shù)abs()為例煮剧,調(diào)用該函數(shù)用以下代碼:

>>> abs(-10)
10

但是斥滤,如果只寫abs呢?

>>> abs
<built-in function abs>

可見勉盅,abs(-10)是函數(shù)調(diào)用佑颇,而abs是函數(shù)本身。要獲得函數(shù)調(diào)用的結(jié)果草娜,我們可以把結(jié)果賦值給變量:

>>> x = abs(-10)
>>> x
10

但是挑胸,如果把函數(shù)本身賦值給變量呢?

>>> f = abs
>>> f
<built-in function abs>

結(jié)論:函數(shù)本身也可以賦值給變量宰闰,即:變量可以指向函數(shù)茬贵。
如果一個變量指向了一個函數(shù),那么就可以通過該變量來調(diào)用這個函數(shù):

>>> f = abs
>>> f(-10)
10

函數(shù)名也是變量
函數(shù)名就是指向函數(shù)的變量移袍!對于abs()這個函數(shù)解藻,完全可以把函數(shù)名abs看成變量,他只想一個可以計算絕對值的函數(shù)咐容。
如果把abs指向其他對象舆逃,會發(fā)生什么?

>>> abs = 10
>>> abs(-10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable

abs指向10后戳粒,就無法通過abs(-10)調(diào)用該函數(shù)了路狮!因為abs變量已經(jīng)不指向求絕對值函數(shù)而是指向一個整數(shù)10。當然實際代碼絕對不能這么些蔚约,這里是為了說明函數(shù)名也是變量奄妨。要恢復(fù)abs函數(shù),請重啟交互環(huán)境苹祟。
注:由于abs函數(shù)實際上是定義在import builtins模塊中的砸抛,所以要讓修改abs變量的指向在其他模塊也生效评雌,要用import builtins; builtins.abs = 10
傳入函數(shù)
既然便利那個可以指向函數(shù)直焙,函數(shù)的參數(shù)就能接收變量景东,那么一個函數(shù)就可以接受另一個函數(shù)作為參數(shù),這種函數(shù)就稱之為高階函數(shù)奔誓。
一個最簡單的高階函數(shù):

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

當我們調(diào)用add(-5, 6, abs)時斤吐,參數(shù)x, y和'f'分別接收-5, 6abs,根據(jù)函數(shù)定義厨喂,我們可以推導(dǎo)計算過程為:

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

小結(jié)
把函數(shù)作為參數(shù)傳入和措,這樣的函數(shù)稱為高階函數(shù),函數(shù)式編程就是指這種高度抽象的編程范式蜕煌。

1.map/reduce

Python中內(nèi)建了map()reduce()函數(shù)派阱。
map()函數(shù)接收兩個參數(shù),一個是函數(shù)斜纪,一個是Iterable贫母,map將傳入的函數(shù)一次作用到序列的每個元素,并把結(jié)果作為新的Iterator返回傀广。
比如颁独,我們有一個函數(shù)f(x)=x2彩届,要把這個函數(shù)作用在一個list[1, 2, 3, 4, 5, 6, 7, 8, 9]上伪冰,就可以用map()實現(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()傳入的第一個參數(shù)是f,即函數(shù)對象本身樟蠕。由于結(jié)果r是一個Iterator, Iterator是惰性序列贮聂,因此通過list()函數(shù)讓他把整個序列都計算出來并返回一個list。
你可能會想寨辩,我寫一個循環(huán)吓懈,也可以計算結(jié)果

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

的確可以,但是從上面的循環(huán)代碼靡狞,并不能一眼看明白“把f(x)作用在list的每一個元素并把結(jié)果生成一個新的list”耻警。所以,map()作為高階函數(shù)甸怕,事實上他把運算規(guī)則抽象了甘穿,因此,我們不但可以計算簡單的f(x)=x2梢杭,還可以計算任意復(fù)雜的函數(shù)温兼,比如,把這個list所有數(shù)字轉(zhuǎn)為字符串:

>>> list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
['1', '2', '3', '4', '5', '6', '7', '8', '9']

reduce把一個函數(shù)作用在一個序列[x1, x2, x3, ...]上武契,這個函數(shù)必須接收兩個參數(shù)募判,reduce把結(jié)果繼續(xù)和序列的下一個元素做累積計算荡含,其效果就是:

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

比如說對一個序列求和,就可以用reduce實現(xiàn):

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

當然求和運算可以直接用Python內(nèi)置函數(shù)sum()届垫,沒必要用reduce释液。但是如果要把序列[1, 3, 5, 7, 9]變成整數(shù)13579reduce就可以派上用場:

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

這個例子本身沒多大用處装处,但是均澳,如果考慮到字符串str也是一個序列,對上面的例子稍加改動符衔,配合map()找前,我們就可以寫出把str轉(zhuǎn)換為int的函數(shù):

>>> from functools import reduce
>>> def fn(x, y):
...     return x * 10 + y
...
>>> def char2num(s):
...     digits = {'0' : 0,'1' : 1,'2' : 2,'3' : 3,'4' : 4,'5' : 5,'6' : 6,'7' : 7,'8' : 8,'9' : 9}
...     return digits[s]
...
>>> reduce(fn, map(char2num, '13579'))
13579

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

from functools import reduce

DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}

def str2int(s):
    def fn(x, y):
        return x * 10 + y
    def char2num(s):
        return DIGITS[s]
    return reduce(fn, map(char2num, s))

進一步簡化

from functools import reduce

DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}

def char2num(s):
    return DIGITS[s]

def str2int(s):
    return reduce(lambda x, y: x * 10 + y, map(char2num, s))

filter

Python內(nèi)建的filter()函數(shù)用于過濾序列。
map()類似判族,filter()也接收一個函數(shù)和一個序列躺盛。和map()不同的是,filter()把傳入的函數(shù)一次作用于每個元素形帮,然后根據(jù)返回值是True還是False決定保留還是丟棄該元素槽惫。
例如,在一個list中辩撑,刪掉偶數(shù)界斜,只保留奇數(shù),可以這么寫:

>>> 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()這個高階函數(shù)各薇,關(guān)鍵在于正確實現(xiàn)一個“篩選”函數(shù)。注意到filter()函數(shù)返回的是一個Iterator君躺,也就是一個惰性序列峭判,所以要強迫fiflter()完成計算結(jié)果,需要用list()函數(shù)獲得所有結(jié)果并返回list棕叫。
sorted
排序算法
排序也是在程序中經(jīng)常用到的算法林螃。無論使用冒泡排序還是快速排序,排序的核心是比較兩個元素的大小俺泣。如果是數(shù)字疗认,我們可以直接比較,但是如果是字符串或者是兩個dict呢伏钠?直接比較數(shù)學上的大小是沒有意義的横漏,因此,比較的過程必須通過函數(shù)抽象出來贝润。
Python內(nèi)置的sorted()函數(shù)就可以對list進行排序:

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

此外绊茧,sorted()函數(shù)也是一個高階函數(shù),他還可以接收一個key函數(shù)來實現(xiàn)自定義的排序打掘,例如按照絕對值大小排序:

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

key指定的函數(shù)將作用于list的每一個元素上华畏,并根據(jù)key函數(shù)返回的結(jié)果進行排序鹏秋。對比原始的list和經(jīng)過key=abs處理過的list:

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

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

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

默認情況下,對字符串排序亡笑,是按照ASCII的大小比較的侣夷,由于'Z' < 'a',結(jié)果仑乌,大寫字母Z會排在小寫字母a前面

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末百拓,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子晰甚,更是在濱河造成了極大的恐慌衙传,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厕九,死亡現(xiàn)場離奇詭異蓖捶,居然都是意外死亡,警方通過查閱死者的電腦和手機扁远,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門俊鱼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人畅买,你說我怎么就攤上這事并闲。” “怎么了谷羞?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵帝火,是天一觀的道長。 經(jīng)常有香客問我洒宝,道長购公,這世上最難降的妖魔是什么萌京? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任雁歌,我火速辦了婚禮,結(jié)果婚禮上知残,老公的妹妹穿的比我還像新娘靠瞎。我一直安慰自己,他們只是感情好求妹,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布乏盐。 她就那樣靜靜地躺著,像睡著了一般制恍。 火紅的嫁衣襯著肌膚如雪父能。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天净神,我揣著相機與錄音何吝,去河邊找鬼溉委。 笑死,一個胖子當著我的面吹牛爱榕,可吹牛的內(nèi)容都是我干的瓣喊。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼黔酥,長吁一口氣:“原來是場噩夢啊……” “哼藻三!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起跪者,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤棵帽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后渣玲,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體岖寞,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年柜蜈,在試婚紗的時候發(fā)現(xiàn)自己被綠了仗谆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡淑履,死狀恐怖隶垮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情秘噪,我是刑警寧澤狸吞,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站指煎,受9級特大地震影響镇饺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜米愿,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一致扯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧像街,春花似錦黎棠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至畴栖,卻和暖如春随静,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吗讶。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工燎猛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留叼丑,地道東北人。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓扛门,卻偏偏與公主長得像鸠信,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子论寨,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

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

  • 直接上正文 函數(shù)是Python內(nèi)建支持的一種封裝葬凳,我們通過把大段代碼拆成函數(shù)绰垂,通過一層一層的函數(shù)調(diào)用,就可以把復(fù)雜...
    OzanShareing閱讀 448評論 0 0
  • 文章來源 基本是拷貝的內(nèi)容火焰,目的是為了將知識點整理在一起劲装。除了一個小結(jié)(裝飾器部分)提供了我自己的解法,其余基本沒...
    王詩翔閱讀 632評論 0 1
  • 函數(shù)式編程就是一種抽象程度很高的編程范式昌简,純粹的函數(shù)式編程語言編寫的函數(shù)沒有變量占业,因此,任意一個函數(shù)纯赎,只要輸入是確...
    齊天大圣李圣杰閱讀 1,530評論 0 2
  • 函數(shù)是Python內(nèi)建支持的一種封裝谦疾,我們通過把大段代碼拆成函數(shù),通過一層一層的函數(shù)調(diào)用犬金,就可以把復(fù)雜任務(wù)分解成簡...
    祐吢房_2c9a閱讀 392評論 1 1
  • 高階函數(shù) 變量可以指向函數(shù) 變量f指向了abs函數(shù)本身念恍。直接調(diào)用abs()函數(shù)和調(diào)用變量f()完全相同。 函數(shù)名也...
    抽象語法樹閱讀 218評論 0 0