淺談Swift中的函數式

對於一個數學專業畢業的學生,函數式編程自然的吸引力了我,從哥德爾的不完備定理,到邱奇的lambda演算,到柯里的組合子邏輯,無不吸引着我。 而swift做爲一門多編程範式的語言,一樣支持函數式編程。 不過函數式編程比較複雜,我也只是管中窺豹,談談本身在swift中的認識。node

函數式的數據結構

函數式編程和命令式編程不同,進行純函數式編程,因爲沒法進行賦值操做(不可變的數據結構),而在C或者是C++這樣的語言中數據結構每每都是可變的,因此之前所學的數據結構和算法都沒有什麼用。 因爲數據結構的不一樣,禁止賦值,有額外的開銷,算法也不同。算法

Binary Search Tree 插入算法

常見的數據結構編程

public class TreeNode {
    public var val: Int
    public var left: TreeNode?
    public var right: TreeNode?
    public init(_ val: Int) {
        self.val = val
        self.left = nil
        self.right = nil
    }
}
複製代碼

和它的插入算法swift

func insertIntoBST(_ root: TreeNode?, _ val: Int) -> TreeNode? {
        guard let rootNode = root  else {
            return TreeNode(val)
        }
        if val < rootNode.val {
            rootNode.left = insertIntoBST(rootNode.left, val)
        } else {
            rootNode.right = insertIntoBST(rootNode.right, val)
        }
        return root
    }
複製代碼

用函數式的數據結構表示api

indirect enum BST {
    case leaf
    case node(BST,Int,BST)
    
    init() {
        self = .leaf
    }
    init(_ value: Int) {
        self = .node(.leaf,value,.leaf)
    }
}
複製代碼

一樣的,函數式的插入算法數據結構

func insertIntoBST(_ root:BST, _ val:Int) -> BST {
        switch root {
        case .leaf:
            return BST(val)
        case let .node(left, value, right):
            if val < value {
                return BST.node(insertIntoBST(left, val), value, right)
            } else {
                return BST.node(left, value, insertIntoBST(right, val))
            }
        }
    }
複製代碼

能夠看到因爲不可變的數據結構,不能對樹作修改,要實現插入算法,必須每次都建立新的樹。app

一等函數

第一次接觸swift最大的印象就是函數是一等公民,能夠做爲參數傳遞。數據結構和算法

好比下面這個fibF函數式編程

func fib(_ n:Int) -> Int {
    if n < 2 {
        return n
    } else {
        return fib(n-1) + fib(n-2)
    }
}
let fibF = fib
fibF(11)
複製代碼

或者咱們不想顯式的定義fib這個函數,使用Z組合子函數

func Z<T,U>(f:@escaping ((T) -> U, T) -> U) -> (T) -> U {
    return {(x:T) -> U in f(Z(f: f),x)}
}
let fibZ = Z(f: {$1 < 2 ? $1 : $0($1-1) + $0($1-2)})
fibZ(11)
複製代碼

在系統提供的方法中也很常見,好比Array的sorted方法就能夠傳一個函數

[1,2,3,4,5].sorted(by: >)
複製代碼

一等函數的概念源自邱奇的lambda演算。 現代計算機採用的是馮諾伊曼結構,而馮諾依曼結構是圖靈機的一個實現。然而在圖靈爲了解決斷定性問題引入圖靈機,和他同時代的天才,邱奇,在幾個月前用lambda演算和遞歸函數,證實了相似的論題。這三個模型(圖靈機,lambda演算和遞歸函數)計算能力等價(邱奇-圖靈猜測)。

算子

高階函數 map與flatMap

什麼函數式編程呢,可能不少人最大的印象就是使用map和flatMap這樣的高階函數,固然這只是一部分

在swift中OptionalsCollection 有map和flatMap函數 map函數

(1...10).map({"\($0)"}) 
複製代碼

flatMap函數

["Hi","Swift"].flatMap({$0})
複製代碼

Functor與Monad

爲何OptionalsArray會有一樣的名稱的方法? 這些map和flatMap方法是否遵照相同的邏輯?

咱們能夠看下swift中對Optionals的map函數的定義

@inlinable public func map<U>(_ transform: (Wrapped) throws -> U) rethrows -> U?
複製代碼

Array的map函數定義

@inlinable public func map<T>(_ transform: (Bound) throws -> T) rethrows -> [T]
複製代碼

Haskell中Functor有個函數fmap(swift中的map)

fmap :: (a -> b) -> [a] -> [b]
複製代碼

因此它們被稱爲Functor(函子)

相應的查看一下swift中對flatMap的定義

//Optionals
@inlinable public func flatMap<U>(_ transform: (Wrapped) throws -> U?) rethrows -> U?
//Array
@inlinable public func flatMap<SegmentOfResult>(_ transform: (Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Element] where SegmentOfResult : Sequence
複製代碼

Haskell中Monad對函數>>=的定義(swift中的flatMap)

(>>=) :: m a -> (a -> m b) -> m b
複製代碼

翻譯成swift就是(僞代碼)

func flatMap<A,B>(x:F<A>)(_ transform: (A) -> F<B>) -> F<B>
複製代碼

可見OptionalsArray都是Monad

面向對象與函數式

swift雖然支持函數式編程,然而在實際開發的時候,純函數編程並不是好的選擇,由於傳統的算法和數據結構都是以圖靈機和過程式語言爲基礎,並且因爲數據的不可變性放棄了執行機能夠反覆擦寫內存屬性,因此並不能作到高效的算法,不能保證性能,可是好在swift是一門支持多編程範式的語言,在合適的地方使用合適的方法纔是咱們須要去作的。

參考資料

純函數數據結構

函數式 Swift

康托爾、哥德爾、圖靈——永恆的金色對角線

函數式編程的早期歷史

Functor、Applicative 和 Monad

Haskell

本文版權屬於再惠研發團隊,歡迎轉載,轉載請保留出處。@白爾摩斯

相關文章
相關標籤/搜索