Swift中的函數式編程

一. 背景簡介

  • 最近很多同學問關于ReactiveCocoa的問題, 所有打算寫一個相關系列的文章,當然目前iOS主流編程語言正在向Swift轉變,我會直接寫RxSwift。
  • 但是在自己準備下手的時候,發(fā)現如果能夠理解函數式編程,對于后面理解響應式編程會很有幫助。
  • 同時Swift也是支持函數式編程的,因此打算先寫一個函數式編程系列,后續(xù)再更新RxSwift
  • 如果對該系列有興趣, 歡迎點擊關注.

二. 需求的解決和思考?

  • 我們從一個示例程序說起
  • 示例:
    • 有一個數組, 數組中存放很多數字
    • 需求: 從數組中帥選出所有的偶數
// 定義數組(當然其他數字也可以)
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)
  • 方案解決了問題, 但是是否有缺陷呢?
    • 如果我希望得到所有的奇數應該怎么辦?
    • 對! 復制一份, 或者直接在循環(huán)中判斷.
    • 那么, 如果我想獲得3的倍數, 4的倍數, 5的倍數數字呢? 復制多份? 顯然并不合理.
    • 對! 擴展性非常的差!
  • 其實Swift提供了一個非常簡單的API, 你可以根據自己的需要獲取想要的數字.
    • 代碼如下:
// 解決方案二:
let evens1 = numbers.filter { (num : Int) -> Bool in
    return num % 2 == 0
}
  • 方法的解析
    • filter接受一個閉包參數
    • 閉包本身有一個Int類型參數, 表示數組中的數字
    • 返回值是一個Bool類型. 用于過濾符合條件的數字
    • 當滿足條件時, 就會將滿足條件的數字放入到數組中
  • 這樣做有什么好處?
    • 如果我選擇獲取奇數, 只需要將==改成!=
    • 如果我希望獲取3/4/5的倍數, 只需要改變2
  • 甚至我們的代碼還可以這樣寫:
    • Swift閉包的簡單寫法而已, $0表示用于獲取第一個閉包參數
    • 不熟練可以暫時忽略這種寫法
let evens2 = numbers.filter { $0 % 2 == 0 }

三. 什么是函數式編程?

  • 什么是函數式編程呢?
    • 函數式編程其實是一種編程思想, 代碼寫出來只是它的表現形式.
    • 在面向對象的編程思想中, 我們將要解決的一個個問題, 抽象成一個個類, 通過給類定義屬性和方法, 讓類幫助我們解決需要處理的問題.(其實面向對象也叫命令式編程, 就像給對象下一個個命令)
    • 而在函數式編程中, 我們則通過函數描述我們要解決的問題, 以及解決問題需要怎樣的方案.
    • 函數本身可以作為變量, 作為參數, 作為返回值(這樣說有一點抽象, 下面的解決方案中就是將函數作為函數的參數)

四. 示例程序分析

  • 面向對象的思考
    • 我現在要對一個數組進行處理, 我可以封裝一個用于處理數組各種情況的工具類
    • 工具類中我提供下面幾個方法
    • 1> 獲取該數組所有的偶數
    • 2> 獲取該數組所有的奇數
    • 3> 獲取數組中其他數字
    • 當然你也可以讓調用方法的時候多傳遞幾個參數, 來確定我獲取的到底是什么, 以便于讓內部進行處理.
    • 但是工具類不可能考慮到各種情況, 另外到底要對數組進行怎樣的處理, 其實調用者最清楚.
    • 那么為何不讓調用者把要做怎樣的操作給我傳遞過來呢?
  • 函數式編程的思考
    • 如果系統(tǒng)沒有filter函數, 我們可以自己給Array擴充一個這樣的函數
    • 封裝一個函數, 函數的參數是一個對數組中數字的操作.
    • 這個數字到底是 %2 %3 %4, 將這樣的操作傳遞進去
    • 既然是一個操作, 操作本身就是一個函數
    • 所有, 函數的參數是一個函數.(沒錯, 讓函數作為函數的操作)
    • 代碼如下:
// 給系統(tǒng)Array擴充函數
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
    }
}

// 用自己的函數解決問題
let evens3 = numbers.myOwnFilter { (num : Int) -> Bool in
    return num % 2 == 0
}
  • 代碼解析:

    • 擴充的函數要求傳遞的是一個閉包, 閉包其實就是一個特殊的函數. 因此, 擴充的函數傳遞的是另外一個函數
    • 在擴充的函數中我們通過傳遞的函數來判斷數字是否符合需求, 符合需求, 則加入數組中.
    • 這樣我們就可以根據用戶自定義的需求來過濾需要的數字了.
  • 如果我們的寫的更有擴充性, 可以使用泛型

    • 比如: 獲取字符串數組中包含"w"的字符串
    • 這個時候需要這樣來修改我的函數
// Array擴充方法
extension Array {
    func myOwnFilter(oprationFunc : (Element) -> Bool) -> [Element] {
        var tempArray = [Element]()

        for item in self {
            if oprationFunc(item) {
                tempArray.append(item)
            }
        }

        return tempArray
    }
}

// 獲取所有的偶數
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")
}

五. 函數式編程有什么用?

  • 函數式編程最早誕生于1958年, 在Lisp語言使用.
  • Lisp是什么?
    • Lisp有各種神奇的傳說, 比如天才程序員通常使用Lisp, 比如用其他語言實現一個功能需要上千行的代碼. 用Lisp只需要少許的代碼.
    • Lisp目前并沒有流行起來, 這可以說不是一門編程語言, 而是一門數學課.
    • Lisp被應用比較廣泛的場景還是在人工智能中
  • 石器時代的函數式編程, 是否有學習的必要呢?
    • 其實目前非?;鸬恼Z言包括Python、Ruby、Javascript, 對函數式編程的支持都很強, 就連java、PHP都有加入匿名函數(本質也是一種函數式編程)
    • 而2014發(fā)布的Swift, 就以支持函數式編程作為一大特點.
    • 函數式編程是否會成為下一個主流的編程范式, 我們不得而知. 但是未來的程序員必然得或多或少懂一點函數式編程. 才能寫出更優(yōu)秀的代碼.
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容