一. 背景簡介
- 最近很多同學(xué)問關(guān)于
ReactiveCocoa
的問題谴咸, 所有打算寫一個相關(guān)系列的文章,當(dāng)然目前iOS主流編程語言正在向Swift轉(zhuǎn)變骗露,我會直接寫RxSwift
岭佳。 - 但是在自己準(zhǔn)備下手的時候,發(fā)現(xiàn)如果能夠理解
函數(shù)式編程
萧锉,對于后面理解響應(yīng)式編程會很有幫助珊随。 - 同時Swift也是支持函數(shù)式編程的,因此打算先寫一個函數(shù)式編程系列柿隙,后續(xù)再更新RxSwift
- 如果對該系列有興趣, 歡迎點擊關(guān)注.
二. 需求的解決和思考叶洞?
- 我們從一個示例程序說起
- 示例:
- 有一個數(shù)組, 數(shù)組中存放很多數(shù)字
- 需求: 從數(shù)組中帥選出所有的偶數(shù)
// 定義數(shù)組(當(dāng)然其他數(shù)字也可以)
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
// 解決方案一:
var evens = [Int]()
for n in numbers {
if n % 2 == 0 {
evens.append(n)
}
}
print(evens)
- 方案解決了問題, 但是是否有缺陷呢?
- 如果我希望得到所有的奇數(shù)應(yīng)該怎么辦?
- 對! 復(fù)制一份, 或者直接在循環(huán)中判斷.
- 那么, 如果我想獲得3的倍數(shù), 4的倍數(shù), 5的倍數(shù)數(shù)字呢? 復(fù)制多份? 顯然并不合理.
- 對! 擴展性非常的差!
- 其實Swift提供了一個非常簡單的API, 你可以根據(jù)自己的需要獲取想要的數(shù)字.
- 代碼如下:
// 解決方案二:
let evens1 = numbers.filter { (num : Int) -> Bool in
return num % 2 == 0
}
- 方法的解析
- filter接受一個閉包參數(shù)
- 閉包本身有一個Int類型參數(shù), 表示數(shù)組中的數(shù)字
- 返回值是一個Bool類型. 用于過濾符合條件的數(shù)字
- 當(dāng)滿足條件時, 就會將滿足條件的數(shù)字放入到數(shù)組中
- 這樣做有什么好處?
- 如果我選擇獲取奇數(shù), 只需要將
==
改成!=
- 如果我希望獲取3/4/5的倍數(shù), 只需要改變
2
- 如果我選擇獲取奇數(shù), 只需要將
- 甚至我們的代碼還可以這樣寫:
- Swift閉包的簡單寫法而已, $0表示用于獲取第一個閉包參數(shù)
- 不熟練可以暫時忽略這種寫法
let evens2 = numbers.filter { $0 % 2 == 0 }
三. 什么是函數(shù)式編程?
- 什么是函數(shù)式編程呢?
- 函數(shù)式編程其實是一種編程思想, 代碼寫出來只是它的表現(xiàn)形式.
- 在面向?qū)ο蟮木幊趟枷胫? 我們將要解決的一個個問題, 抽象成一個個類, 通過給類定義屬性和方法, 讓類幫助我們解決需要處理的問題.(其實面向?qū)ο笠步忻钍骄幊? 就像給對象下一個個命令)
- 而在函數(shù)式編程中, 我們則通過函數(shù)描述我們要解決的問題, 以及解決問題需要怎樣的方案.
- 函數(shù)本身可以作為變量, 作為參數(shù), 作為返回值(這樣說有一點抽象, 下面的解決方案中就是將函數(shù)作為函數(shù)的參數(shù))
四. 示例程序分析
- 面向?qū)ο蟮乃伎?
- 我現(xiàn)在要對一個數(shù)組進行處理, 我可以封裝一個用于處理數(shù)組各種情況的工具類
- 工具類中我提供下面幾個方法
- 1> 獲取該數(shù)組所有的偶數(shù)
- 2> 獲取該數(shù)組所有的奇數(shù)
- 3> 獲取數(shù)組中其他數(shù)字
- 當(dāng)然你也可以讓調(diào)用方法的時候多傳遞幾個參數(shù), 來確定我獲取的到底是什么, 以便于讓內(nèi)部進行處理.
- 但是工具類不可能考慮到各種情況, 另外到底要對數(shù)組進行怎樣的處理, 其實調(diào)用者最清楚.
- 那么為何不讓調(diào)用者把要做怎樣的操作給我傳遞過來呢?
- 函數(shù)式編程的思考
- 如果系統(tǒng)沒有filter函數(shù), 我們可以自己給Array擴充一個這樣的函數(shù)
- 封裝一個函數(shù), 函數(shù)的參數(shù)是一個對數(shù)組中數(shù)字的操作.
- 這個數(shù)字到底是
%2 %3 %4
, 將這樣的操作傳遞進去 - 既然是一個操作, 操作本身就是一個函數(shù)
- 所有, 函數(shù)的參數(shù)是一個函數(shù).(沒錯, 讓函數(shù)作為函數(shù)的操作)
- 代碼如下:
// 給系統(tǒng)Array擴充函數(shù)
extension Array {
func myOwnFilter(oprationFunc : (Int) -> Bool) -> [Int] {
var tempArray = [Int]()
for item in self {
if oprationFunc(item as! Int) {
tempArray.append(item as! Int)
}
}
return tempArray
}
}
// 用自己的函數(shù)解決問題
let evens3 = numbers.myOwnFilter { (num : Int) -> Bool in
return num % 2 == 0
}
-
代碼解析:
- 擴充的函數(shù)要求傳遞的是一個閉包, 閉包其實就是一個特殊的函數(shù). 因此, 擴充的函數(shù)傳遞的是另外一個函數(shù)
- 在擴充的函數(shù)中我們通過傳遞的函數(shù)來判斷數(shù)字是否符合需求, 符合需求, 則加入數(shù)組中.
- 這樣我們就可以根據(jù)用戶自定義的需求來過濾需要的數(shù)字了.
-
如果我們的寫的更有擴充性, 可以使用泛型
- 比如: 獲取字符串?dāng)?shù)組中包含"w"的字符串
- 這個時候需要這樣來修改我的函數(shù)
// Array擴充方法
extension Array {
func myOwnFilter(oprationFunc : (Element) -> Bool) -> [Element] {
var tempArray = [Element]()
for item in self {
if oprationFunc(item) {
tempArray.append(item)
}
}
return tempArray
}
}
// 獲取所有的偶數(shù)
let evens3 = numbers.myOwnFilter { (num : Int) -> Bool in
return num % 2 == 0
}
// 獲取所有帶"w"的字符串
let strs = ["why", "lmj", "lnj", "yz", "wff", "sws"]
strs.myOwnFilter { (str : String) -> Bool in
return str.containsString("w")
}
五. 函數(shù)式編程有什么用?
- 函數(shù)式編程最早誕生于1958年, 在Lisp語言使用.
- Lisp是什么?
- Lisp有各種神奇的傳說, 比如天才程序員通常使用Lisp, 比如用其他語言實現(xiàn)一個功能需要上千行的代碼. 用Lisp只需要少許的代碼.
- Lisp目前并沒有流行起來, 這可以說不是一門編程語言, 而是一門數(shù)學(xué)課.
- Lisp被應(yīng)用比較廣泛的場景還是在人工智能中
- 石器時代的函數(shù)式編程, 是否有學(xué)習(xí)的必要呢?
- 其實目前非常火的語言包括Python禀崖、Ruby衩辟、Javascript, 對函數(shù)式編程的支持都很強, 就連java俩莽、PHP都有加入匿名函數(shù)(本質(zhì)也是一種函數(shù)式編程)
- 而2014發(fā)布的Swift, 就以支持函數(shù)式編程作為一大特點.
- 函數(shù)式編程是否會成為下一個主流的編程范式, 我們不得而知. 但是未來的程序員必然得或多或少懂一點函數(shù)式編程. 才能寫出更優(yōu)秀的代碼.