python學習筆記|函數(shù)式編程——map,filter,reduce

開始陸續(xù)學習python相關知識想括,這一篇介紹函數(shù)式編程相關知識司致。在文章中會介紹三個主要的高階函數(shù),從基本例子入手了解函數(shù)式編程肛鹏。

map()

map 意思是映射逸邦,在數(shù)學的學習中,映射即為x與y存在的關系在扰,即x通過一定的方法稱為y缕减。首先來看一個例子:將數(shù)組[1,2芒珠,3桥狡,4,5皱卓,6裹芝,7,8]中的元素均變?yōu)樵瓉淼?倍娜汁。

乍一看很簡單嫂易,通過一個簡單的循環(huán)便可以解決。那么我們首先用這個方法解決:

def num2(list):
    newList = []
    for x in list:
        x *= 2
        newList.append(x)
    return newList

執(zhí)行方法:

list = [1,2,3,4,5,6,7,8]
num2(list)

輸出結(jié)果為:

[1,4,9,16,25,36,49,64]

上述方法為整形掐禁,若將其擴展為字符或字符串呢怜械,我們再來看一個例子——大小寫轉(zhuǎn)換颅和。

為了簡單,我們只將['a','b','c','d']轉(zhuǎn)換為['A','B','C','D']

def upperNum(list):
    newList = []
    for x in list:
        newList.append(x[:].upper())
    return newList

執(zhí)行函數(shù):

list = ['a','b','c','d']
print upperNum(list)

輸出結(jié)果為:

['A', 'B', 'C', 'D']

通過上述兩個例子缕允,你是否發(fā)現(xiàn)了相同點峡扩。均是一個數(shù)組中元素通過某種方法進行變換,唯一不同的是傳入數(shù)組的類型與方法不同障本。那么教届,我們能否定義出這個相同點函數(shù),將數(shù)組和方法作為參數(shù)傳遞進去彼绷。

幸運的是巍佑,泛型在python中非常簡單茴迁,這讓我們無需考慮數(shù)組中變量的類型寄悯。關于泛型的討論,在下面補充中會介紹堕义,如果你了解其他編程語言相信一定不會陌生猜旬。

而如何將函數(shù)作為參數(shù)傳遞進去,這便是函數(shù)式編程倦卖。

函數(shù)式編程就是一種抽象程度很高的編程范式洒擦,純粹的函數(shù)式編程語言編寫的函數(shù)沒有變量,因此怕膛,任意一個函數(shù)熟嫩,只要輸入是確定的,輸出就是確定的褐捻,這種純函數(shù)我們稱之為沒有副作用掸茅。而允許使用變量的程序設計語言,由于函數(shù)內(nèi)部的變量狀態(tài)不確定柠逞,同樣的輸入昧狮,可能得到不同的輸出,因此板壮,這種函數(shù)是有副作用的逗鸣。

python對函數(shù)式編程提供部分支持,既可以作為參數(shù)傳遞绰精,又能返回一個函數(shù)撒璧。由于python允許使用變量,因此笨使,python不是純函數(shù)式編程語言卿樱。

我們來定義一個map函數(shù),其參數(shù)為function方法和sequence數(shù)組阱表,而通過研究發(fā)現(xiàn)其返回值依然是是一個數(shù)組殿如。

def map(function,sequence):
    newList = []
    for x in sequence:
        newList.append(function(x))
    return newList

上述兩個通過map方法便能合二為一:

def num2(x):
  return x * x

def upperNum(x):
    return x[:].upper()

map(num2,list)
map(upperNum,list)

執(zhí)行結(jié)果仍然為:

[1,4,9,16,25,36,49,64]
['A', 'B', 'C', 'D']

注贡珊,在python2中是能夠直接出來結(jié)果的,但是在python3中這個會返回一個對象涉馁。要想用到結(jié)果就必須的在前面加上list來轉(zhuǎn)化一下门岔,比如:

在系統(tǒng)庫中的map函數(shù)定義如下,有一個可選參數(shù)

map(function, sequence, *sequence_1)

如果給定多個序列烤送,則函數(shù)被調(diào)用寒随,其中包含相應的參數(shù)列表每個序列的項;當不是全部時帮坚,用None代替缺失值使得序列的長度相同妻往。如果函數(shù)沒有,返回一個列表序列的項(或一個數(shù)組的列表试和,如果不止一個序列)讯泣。

例如:

 l1 = [ 0, 1, 2, 3, 4, 5, 6 ]  
 l2 = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ]  
 map(f, l1, l2)  

結(jié)果為:

[(0, 'Sun'), (1, 'Mon'), (2, 'Tue'), (3, 'Wed'), (4, 'Thu'), (5, 'Fri'), (6, 'Sat')]  

補充中提到一個匿名函數(shù),對map方法仍能進行進一步的簡化阅悍。

filter()

filter 意思是過濾器好渠,即為篩選出數(shù)組或元祖中符合條件的元素。首先來看一個例子节视,選出1-10中的奇數(shù):

def is_Odd(list):
    newList = []
    for x in list:
        if x%2 :
            newList.append(x)
        else:
            pass
    return newList

執(zhí)行函數(shù):

list = [1,2,3,4,5,6,7,8,9]
is_Odd(list)

輸出結(jié)果為:

[1, 3, 5, 7, 9]

再來看另一個例子拳锚,假設有一個標記文件路徑的數(shù)組字符串數(shù)組exampleFiles賦值如下:

exampleFiles = ["README.md", "HelloWorld.py","HelloSwift.swift", "HelloPython.py"]

現(xiàn)在我們想從數(shù)組中取出.py數(shù)組,使用一個循環(huán)便可得到:

def getPyFile(fileNames):
  newFileNames = []
  for fileName in fileNames:
      if ".py" in fileName:
         newFileNames.append(fileName)
  return newFileNames

執(zhí)行這個函數(shù):

fileNames =  ["README.md", "HelloWorld.py","HelloSwift.swift", "HelloPython.py"]
getPyFile(fileNames)

結(jié)果為:

['HelloWorld.py', 'HelloPython.py']

從上述兩個例子中可以得出兩個方法的共同點寻行,即返回那些函數(shù)(項)為真的序列項霍掺。如果函數(shù)是None,返回True的項拌蜘,并返回一個列表杆烁。(如果序列是一個元組或者字符串,返回相同的類型)

因此拦坠,有了上述map函數(shù)的基礎连躏,我們可以定義一個過濾器方法,將上述方法合二為一:

def filter(function,sequence):
    list = []
    for x in sequence:
        if function(x):
            list.append(x)
    return list

以篩選文件為例贞滨,執(zhí)行這個方法:

def getPyFile2(fileName):
  if ".py" in fileName:
       return fileName
   else:
       pass

filter(getPyFile2,fileNames)

執(zhí)行這個函數(shù)入热,結(jié)果為:

['HelloWorld.py', 'HelloPython.py']

與map相同,在python3中filter函數(shù)返回的是一個對象晓铆,需要加list轉(zhuǎn)換成數(shù)組勺良。

reduce()

reduce意思是聚合,有了map和filter函數(shù)的研究基礎骄噪,我們同樣先來討論一個簡單的函數(shù)尚困,定義一個函數(shù)計算數(shù)組中所有整數(shù)的和。

python函數(shù)庫中雖然自帶有sum()函數(shù)链蕊,但我們?nèi)宰远x函數(shù)解決

def sum(list):
    result = 0
    for x in list:
        result += x
    return result

執(zhí)行函數(shù):

list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
sum(list)

結(jié)果為:

45

再來看另外一個例子:數(shù)以一組單詞事甜,將其拼成一個句子谬泌。例如將數(shù)組["I","am","a","good","boy"]拼接成一個字符串"I am a good boy"

定義函數(shù)如下:

def append(list):
    result = ""
    for x in list:
        result = result + x+" "
    return result

執(zhí)行函數(shù):

list = ["I","am","a","good","boy"]
append(list)

結(jié)果為:

"I am a good boy "

分析上面兩個函數(shù)相同的地方,它們都用一些值初始化了一個變量result逻谦,它們進行處理的時候都遍歷了整個數(shù)組list掌实,并通過某種算法更新result。要定義實現(xiàn)這種通用算法的一個通用函數(shù)邦马,有兩處需要進行抽象:result的初始值以及在每個循環(huán)中用于更新result值的函數(shù)贱鼻。

因此我們可以定義一個函數(shù)函數(shù)滿足上述需求,注意reduce函數(shù)最終返回的是一個value值而非數(shù)組:

def reduce(function,sequence):
    result = None
    for x in sequence:
        result = function(result,x)
    return result

其中function為需要操作的函數(shù)滋将,sequence為數(shù)組邻悬,將resultsequence中元素操作后值賦予result,進行循環(huán)操作随闽。

執(zhí)行上述函數(shù)操作便得以簡化:

def sum(a,b):
    return  a + b

def append(a,b):
    return  a + " " + b

reduce(sum,list)
reduce(append,list)

這我們得出所需要的reduce函數(shù)父丰。在系統(tǒng)中的reduce定義為 reduce(function,sequence,initial=None),由于初值并非只有0或空字符串橱脸,可以為任意础米,因此需要賦初值,在這里給予一個初值變量并默認初值添诉。將上述reduce函數(shù)改編為:

def reduce(function,sequence,initial=None):
    result = initial
    for x in sequence:
        result = function(result,x)
    return result

補充

1.匿名函數(shù)

在Python中,對匿名函數(shù)提供了有限支持医寿。還是以map()函數(shù)為例栏赴,計算f(x)=x2時,需定義一個f(x)的函數(shù)靖秩,然后使用map函數(shù)须眷,如:

def f(x):
  return x * x

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

輸出結(jié)果為:

[1, 4, 9, 16, 25, 36, 49, 64, 81]

使用匿名函數(shù),便能用一行解決:

list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))

輸出結(jié)果同樣為:

[1, 4, 9, 16, 25, 36, 49, 64, 81]

從上述例子可以看到沟突,匿名函數(shù)關鍵字為lambda花颗,冒號前面的x表示函數(shù)參數(shù)。匿名函數(shù)有個限制惠拭,就是只能有一個表達式扩劝,不用寫return,返回值就是該表達式的結(jié)果职辅。

用匿名函數(shù)有個好處棒呛,因為函數(shù)沒有名字,不必擔心函數(shù)名沖突域携。此外簇秒,匿名函數(shù)也是一個函數(shù)對象,也可以把匿名函數(shù)賦值給一個變量秀鞭,再利用變量來調(diào)用該函數(shù)趋观,例如:

f = lambda x: x * x
f(5)

結(jié)果為:

25

因此扛禽,上述幾段程序使用匿名函數(shù)便能簡化很多:

filter(lambda fileName:".py" in fileName,fileNames)
reduce(lambda a, b: a + b,list)
reduce(lambda a, b: a + " " +b,list)

2.sorted函數(shù)

將函數(shù)作為參數(shù)可不僅僅上述幾個函數(shù),比如sorted函數(shù)皱坛,顧名思義為排序旋圆,使用起來也很簡單。

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

在系統(tǒng)中麸恍,sort函數(shù)的定義為:

def sorted(iterable, cmp=None, key=None, reverse=False)

其高階在于后面的三個參數(shù)灵巧。reverse參數(shù)是一個bool變量,是否倒序抹沪,默認值為False(正序)刻肄。key是關鍵字,是用于排序的對象融欧。在上個例子中為數(shù)組本身敏弃,當然也可以對數(shù)組的個位數(shù)進行排序,如:

sorted([36, 5, 12, 9, 21],key=lambda x:x%10)
[21, 12, 5, 36, 9]

cmp參數(shù)是比較的方法噪馏,加入我們要實現(xiàn)倒序而不是用reverse麦到,可以這樣寫:

sorted([36, 5, -12, 9, -21],cmp=lambda x,y:cmp(x,y))
[-21, -12, 5, 9, 36]

練習

作為本文的結(jié)束,給出一個小例子供大家理解三個函數(shù)欠肾。(題目來源于 objc.io出版的《函數(shù)式編程》)

有一組城市和人口數(shù)據(jù)如下:

name: "Paris", population: 2243
name: "Madrid", population: 3216
name: "Amsterdam", population: 811
name: "Berlin", population: 3397

假如我們想要找出至少有1百萬人口的城市并打印出它們的城市名和人口瓶颠,輸出結(jié)果為:

City: Population 
Paris : 2243000 
Madrid : 3216000 
Berlin : 3397000

首先我們過濾掉小于100萬人口的城市。然后使用map函數(shù)進行影射刺桃,將城市人口的單位進行轉(zhuǎn)換粹淋。最后,我們使用reduce通過城市名和人口的列表計算得出一個字符串瑟慈。這里我們使用了標準庫的map,filterreduce函數(shù)桃移。結(jié)果,我們可以像鏈條一樣將這些函數(shù)串起來葛碧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末借杰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子进泼,更是在濱河造成了極大的恐慌蔗衡,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,919評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缘琅,死亡現(xiàn)場離奇詭異粘都,居然都是意外死亡,警方通過查閱死者的電腦和手機刷袍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評論 3 392
  • 文/潘曉璐 我一進店門翩隧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事堆生∽ú” “怎么了?”我有些...
    開封第一講書人閱讀 163,316評論 0 353
  • 文/不壞的土叔 我叫張陵淑仆,是天一觀的道長涝婉。 經(jīng)常有香客問我,道長蔗怠,這世上最難降的妖魔是什么墩弯? 我笑而不...
    開封第一講書人閱讀 58,294評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮寞射,結(jié)果婚禮上渔工,老公的妹妹穿的比我還像新娘。我一直安慰自己桥温,他們只是感情好引矩,可當我...
    茶點故事閱讀 67,318評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著侵浸,像睡著了一般旺韭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上掏觉,一...
    開封第一講書人閱讀 51,245評論 1 299
  • 那天区端,我揣著相機與錄音,去河邊找鬼履腋。 笑死珊燎,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的遵湖。 我是一名探鬼主播,決...
    沈念sama閱讀 40,120評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼晚吞,長吁一口氣:“原來是場噩夢啊……” “哼延旧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起槽地,我...
    開封第一講書人閱讀 38,964評論 0 275
  • 序言:老撾萬榮一對情侶失蹤迁沫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后捌蚊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體集畅,經(jīng)...
    沈念sama閱讀 45,376評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,592評論 2 333
  • 正文 我和宋清朗相戀三年缅糟,在試婚紗的時候發(fā)現(xiàn)自己被綠了挺智。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,764評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡窗宦,死狀恐怖赦颇,靈堂內(nèi)的尸體忽然破棺而出二鳄,到底是詐尸還是另有隱情,我是刑警寧澤媒怯,帶...
    沈念sama閱讀 35,460評論 5 344
  • 正文 年R本政府宣布订讼,位于F島的核電站,受9級特大地震影響扇苞,放射性物質(zhì)發(fā)生泄漏欺殿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,070評論 3 327
  • 文/蒙蒙 一鳖敷、第九天 我趴在偏房一處隱蔽的房頂上張望脖苏。 院中可真熱鬧,春花似錦哄陶、人聲如沸帆阳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蜒谤。三九已至,卻和暖如春至扰,著一層夾襖步出監(jiān)牢的瞬間鳍徽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評論 1 269
  • 我被黑心中介騙來泰國打工敢课, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留阶祭,地道東北人。 一個月前我還...
    沈念sama閱讀 47,819評論 2 370
  • 正文 我出身青樓直秆,卻偏偏與公主長得像濒募,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子圾结,可洞房花燭夜當晚...
    茶點故事閱讀 44,665評論 2 354

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