對于絕大多數(shù)的程序員來說接觸更多的是面向?qū)ο蠛兔嫦蜻^程的方式進行思考姜性。實際上還存在一種更高效的思考方式函數(shù)式
。也許有很多人聽過髓考,但不一定使用過部念。Swift的語言特性能把函數(shù)式思想發(fā)揮到淋漓盡致,但是對于習(xí)慣了用OC開發(fā)在轉(zhuǎn)向Swift語言的程序元來說绳军,很多人并沒有意思到這一點印机。如果對函數(shù)式思想感興趣的話矢腻,推薦函數(shù)式Swift
這本書门驾。很難給出函數(shù)式一個準確的定義,只能說在函數(shù)式的世界中函數(shù)式中的函數(shù)
不是一個函數(shù)多柑,你可以把函數(shù)看成一個值奶是,就像一般的常量或變量一樣。如果習(xí)慣了這種思維方式竣灌,就會認識到什么是函數(shù)式
聂沙。先從一個1+1的計算來介紹函數(shù)式思想,之后再說標準庫函數(shù)Map
Filter
Reduce
的實現(xiàn)原理初嘹,并仿寫一個標準系統(tǒng)庫的Map
Filter
Reduce
函數(shù)及汉。
一、從 “1+1” 表達式認識函數(shù)式
假設(shè)我們想通過一個方法計算兩個數(shù)之和屯烦,通常會這樣寫:
func add(x: Int , y: Int) -> Int {
return x + y
}
這種方式是很容易想到的坷随,也符合我們正常的思維邏輯,實際上除了這種寫法驻龟,還有另一種函數(shù)式的寫法温眉,先看代碼,然后我來說明這種寫法是什么意思翁狐。
func add(x: Int) -> ((Int) -> (Int)) {
return { y in return x + y }
}
代碼很簡單类溢,但是對于沒接觸函數(shù)式的人而言,還是需要仔細看才能看的懂露懒。接下來請仔細看分析闯冷。對于a+b=c(1+1=2)計算兩個數(shù)之和的這種算法,我們可以這樣想假設(shè)a是一個已知數(shù)懈词,b是一個未知數(shù)窃躲,這時只需要一個確定的函數(shù),以及傳入一個b钦睡,我們就能得到c蒂窒。所以在上面的方法中會出現(xiàn)(Int) -> (Int)這個閉包躁倒,這個閉包我們就可以把它看做是一個函數(shù),在a已知的情況下洒琢,傳入一個數(shù)秧秉,我們就可以知道a加上這個未知數(shù)的結(jié)果。而上面一直提到的a就是add方法中的x衰抑。
第一個add方法的調(diào)用形式是add(1,2)
,對于第二個add方法的調(diào)用象迎,通常是這種形式add(1)(2)
。這兩種調(diào)用形式的意義完全不同呛踊,第二種首先是傳入1砾淌,返回一個閉包,然后將2作為參數(shù)傳入到閉包中谭网,最后得出結(jié)果汪厨。第二種調(diào)用形式的第二步是把:已知數(shù)+未知數(shù)=確定數(shù)
看做是一個函數(shù)進行處理的,這便是函數(shù)式視為方式愉择。這種1+1=2的計算太簡單劫乱,到目前為止也許你還是沒有看到函數(shù)式思維的強大性,別急锥涕,請繼續(xù)往看
二衷戈、溫習(xí)標準庫函數(shù)Map、Filter层坠、Reduce的使用
這里就簡單溫習(xí)一下使用殖妇,不做過多解釋,這不是重點破花,重點是要一步步仿寫出系統(tǒng)庫的Map谦趣、Filter、Reduce函數(shù)旧乞。直接看下面的溫習(xí)代碼
func standardTest() {
/*
Map函數(shù)返回數(shù)組的元素類型不一定要與原數(shù)組相同
Map還能返回判斷數(shù)組中的元素是否滿足某種條件的Bool值數(shù)組
flatMap 與 map 不同之處是
flatMap返回后的數(shù)組中不存在 nil 同時它會把Optional解包;
flatMap 還能把數(shù)組中存有數(shù)組的數(shù)組 一同打開變成一個新的數(shù)組 ;
flatMap也能把兩個不同的數(shù)組合并成一個數(shù)組 這個合并的數(shù)組元素個數(shù)是前面兩個數(shù)組元素個數(shù)的乘
*/
let mapArray1:[Int] = [1,2,3,4]
let mapArray2 = mapArray1.map { (number: Int) -> Int in
return number + 1
}
//簡單的寫法 $0代表mapArray1中的每一個元素
let mapArray3 = mapArray1.map{ $0 + 1 }
print("標準庫函數(shù)測試結(jié)果>>>>> mapArray2:\(mapArray2)")
print("標準庫函數(shù)測試結(jié)果>>>>> mapArray3:\(mapArray3)")
//filter函數(shù)
let filterArray1 = [1,2,3,4]
let filterArray2 = filterArray1.filter { (number:Int) -> Bool in
if number > 2 {
return true
}else {
return false
}
}
print("標準庫函數(shù)測試結(jié)果>>>>> filterArray2:\(filterArray2)")
//reduce函數(shù) 10是初始值 10 + 1 + 2 + 3 + 4
let reduceArray1:[Int] = [1,2,3,4]
//let sum = reduceArray1.reduce(<#T##initialResult: Result##Result#>, <#T##nextPartialResult: (Result, Int) throws -> Result##(Result, Int) throws -> Result#>)
let reduceSum = reduceArray1.reduce(10) { (total, num) -> Int in
return total + num
}
print("標準庫函數(shù)測試結(jié)果>>>>> reduceSum:\(reduceSum)")
}
三蔚润、標準庫函數(shù)Map的實現(xiàn)原理
接下來我會一步步實現(xiàn)Map函數(shù),先從簡單的實現(xiàn)說起尺栖,然后在此基礎(chǔ)上一步步的優(yōu)化嫡纠。我要分三步來實現(xiàn)并優(yōu)化,先看第一步實現(xiàn)延赌。
1除盏、第一步簡單實現(xiàn)
func customMap1(arr: [Int],transform: ((Int) -> (Int))) -> [Int] {
var rs: [Int] = []
for x in arr {
rs.append(transform(x))
}
return rs
}
//代碼調(diào)用形式
let customMapResultArray1 = self.customMap1(arr: customMapArray1) { (num: Int) -> (Int) in
return num + 1
}
代碼很少,實現(xiàn)起來的調(diào)用形式并非是系統(tǒng)庫函數(shù)那種形式挫以。不要著急者蠕,先從簡單的說起,然后一步步的優(yōu)化掐松。注意customMap1方法中有一個transform這樣一個閉包踱侣,這個閉包的功能實際上就是傳入一個Int類型的數(shù)可以映射成另一個數(shù)粪小,也就是函數(shù)式思維中的函數(shù),這里是把這個函數(shù)當做參數(shù)處理的(這是函數(shù)式思維的本質(zhì))抡句。
在customMap1方法中探膊,首先是定義了一個[Int]類型數(shù)組,在遍歷外部傳入的arr數(shù)組的時候待榔,執(zhí)行了這樣一句代碼rs.append(transform(x))
,transform(x)是一個Int類型的數(shù)值逞壁,因為transform是一個參數(shù)為Int類型,返回值為Int類型的閉包锐锣,這里我們只把閉包的返回值保存到rs數(shù)組中腌闯,至于tansform內(nèi)部具體是怎么實現(xiàn)的,這個方法里面并不用管雕憔,只要把它當做參數(shù)來看即可姿骏。至于transform具體是怎樣映射的,完全交由外部來決定橘茉,在調(diào)用的時候涉及映射規(guī)則工腋,但是目前的規(guī)則就是Int類型映射成Int類型姨丈。上面的代碼中映射規(guī)則是將原本的Int類型加1畅卓。
2、第二步優(yōu)化
這一步實現(xiàn)蟋恬,主要是借助Swift中的泛型語法翁潘,可以將Int類型的數(shù)組,映射成為任意類型的數(shù)組歼争。實現(xiàn)代碼如下:
func customMap2<T>(arr: [Int],transform: ((Int) -> (T))) -> [T] {
var rs: [T] = []
for x in arr {
rs.append(transform(x))
}
return rs
}
和第一步相比拜马,僅僅只要把第一步的實現(xiàn)中的返回值改為T即可。至于泛型語法沐绒,這里不做過多解釋俩莽。雖然是可以將Int類型數(shù)組映射成為其他類型的數(shù)組,但是如果想把String類型的數(shù)組映射成為其他類型的數(shù)組乔遮,此時就并不能滿足扮超。如何實現(xiàn),請看下一步優(yōu)化蹋肮。
3出刷、最終優(yōu)化成型
這一步優(yōu)化,主要是借助extension來實現(xiàn)坯辩。實現(xiàn)代碼如下:
extension Array{
//Map的第三部優(yōu)化
func customMap<T>(transform:((Element) -> T)) -> [T]{
var rs:[T] = []
for element in self {
rs.append(transform(element))
}
return rs
}
}
借助于extension我們可以實現(xiàn)任意類型的數(shù)組調(diào)用此方法馁龟,不僅僅局限于[Int]類型數(shù)組。extension是個好東西漆魔,是要好好利用坷檩。調(diào)用形式如下:
let customMapResultArray1 = customMapArray1.customMap { (num:Int) -> String in
return "\(num)"
}
print("自定義Map函數(shù)測試結(jié)果>>>>> customMapResultArray1:\(customMapResultArray1)")
截止目前却音,我們就實現(xiàn)了標準系統(tǒng)庫函數(shù)map的仿寫。代碼還是很簡單的矢炼,主要是這種思維方式的轉(zhuǎn)化僧家,接下來看看Filter以及Reduce函數(shù)的實現(xiàn)。
四裸删、標準庫函數(shù)Filter的實現(xiàn)原理
有了上面對Map函數(shù)的認識八拱,想實現(xiàn)Filter函數(shù),只要簡單的套用就行了涯塔。對于Filter函數(shù)肌稻,無非就是將:一個數(shù)值是否滿摸個條件作為函數(shù)來處理,然后將這個函數(shù)看做一個參數(shù)匕荸。同樣是在extension Array中實現(xiàn)代碼如下:
//Filter的實現(xiàn)
func customFilter(includeElement:(Element) -> Bool) -> [Element] {
var rs:[Element] = []
for x in self {
if includeElement(x) == true {
rs.append(x)
}
}
return rs
}
//調(diào)用形式
let customFilterResultArray1 = customMapArray1.customFilter { (num:Int) -> Bool in
return num > 2
}
print("自定義Filter函數(shù)測試結(jié)果>>>>> customFilterResultArray1:\(customFilterResultArray1)")
五爹谭、標準庫函數(shù)Reduce的實現(xiàn)原理
對于Reduce還是直接上代碼吧。
//Reduce函數(shù)的實現(xiàn)
func customReduce<T>(initial:T, combine:(T,Element)->T) -> T {
var rs = initial
for x in self {
rs = combine(rs, x)
}
return rs
}
外部調(diào)用形式榛搔。
let sum = customMapArray1.customReduce(initial: 10) { (a:Int, b:Int) -> Int in
return a + b
}
print("自定義Reduce函數(shù)測試結(jié)果>>>>> sum:\(sum)")
總結(jié)
函數(shù)式思考方式感覺還是很高效的诺凡,雖然不可能在實際開發(fā)中全部使用函數(shù)式來處理問題,但是在處理默寫問題上践惑,函數(shù)式思維方式絕對會簡單很多腹泌。后期還會繼續(xù)研究函數(shù)式,多多關(guān)注尔觉。上訴實現(xiàn)由代碼凉袱,代碼僅供簡單參考。代碼下載鏈接: https://github.com/ZhengYaWei1992/FunctionThinking1