開始陸續(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ù)組邻悬,將result
與sequence
中元素操作后值賦予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
,filter
和reduce
函數(shù)桃移。結(jié)果,我們可以像鏈條一樣將這些函數(shù)串起來葛碧。