函數(shù)式編程初探之Swift

最近接觸swift之后尘执,發(fā)現(xiàn)一個與Object-C區(qū)別很大的地方是Object-C里面很多Class里面都換成了Struct類型了,在自己有限的知識范圍內(nèi)票从,感覺Struct并沒有Class那么好舶沛,因為大部分Struct內(nèi)存分配在棧上面,Class充分利用了堆棧铐望,似乎感覺Class要好一些,但是為什么swift會用這么多Struct呢茂附?還有一個問題是元組正蛙,當時一個同學(xué)在改寫一個數(shù)組元組的時候,感覺很別扭营曼,整個數(shù)組都要拷貝乒验,難道這樣的效率更高,帶著這樣的疑問再網(wǎng)上找了半天蒂阱,冥冥之中好像聽過“不可變變量”這種說法锻全,最后在函數(shù)式編程當中找到了一些答案,所以本文只是函數(shù)式編程的一個基礎(chǔ)概念理解蒜危,如果有上面不對的地方虱痕,歡迎大家指正。

什么是函數(shù)式編程:

In computer science, functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data.

這個是維基百科的解釋辐赞,簡單的翻譯為:是一種構(gòu)造程序的方式部翘,這種方式將函數(shù)看待為數(shù)學(xué)方程,并且避免使用狀態(tài)和變量响委。

函數(shù)式編程與其他編程方式的區(qū)別也經(jīng)常叫做聲明式編程與命令式編程新思,這兩個的主要區(qū)別是;

命令式編程遇到問題的解決方式是怎么用算法,一步一步的解決這個問題赘风,一個很形象的比喻是你有個一食譜夹囚,教你怎么一步一步的去做一道菜,要哪些原料邀窃,混合哪些東西荸哟,最后吧這道菜做出來。

聲明式編程遇到問題的解決方式是有什么可以解決這個東西,而不是怎么去解決鞍历。如果用一道菜做比喻的話就是直接給你一個照片舵抹,或者告訴你這個菜是什么樣子的。

函數(shù)式編程的一些基本概念:

1變量不可變和副作用

變量不可變是函數(shù)式編程與命令式編程的主要區(qū)別劣砍,如果你需要改變一個變量惧蛹,那就需要重新Copy一份,在進行修改刑枝,Swift中String類型也是值類型香嗓,所以在函數(shù)中傳遞的時候其實是進行了值拷貝(當然是有修改的時候,編譯器對這個做了優(yōu)化)装畅,所以你在函數(shù)中拿到的String可以很放心的去修改這個字符串而不會被別人串改靠娱。官方文檔是這么介紹的:

Swift’s copy-by-default String behavior ensures that when a function or method passes you a String value, it’s clear that you own that exact String value, regardless of where it came from. You can be confident that the string you are passed won’t be modified unless you modify it yourself.

所以這種Copy屬性就會減少一部分Bug的產(chǎn)生,還有一個優(yōu)點是減少了變量的互斥洁灵,增加了多核的運算能力饱岸,知乎上面有一張圖表示了現(xiàn)代計算機計算能力的增長已經(jīng)不依賴CPU主頻的增長,而是依賴CPU核數(shù)的增多徽千,所以這種不可變變量充分利用了這個好處。


aaa.jpg

副作用表示調(diào)用一個函數(shù)因為某種外部的原因?qū)е略趨?shù)一致的情況下返回卻不一樣了汤锨,一般有全局變量的情況會導(dǎo)致副作用双抽,也不允許函數(shù)去改變外部變量的狀態(tài),這樣會導(dǎo)致程序的結(jié)果不一致闲礼。

2.模塊化

函數(shù)式編程吧業(yè)務(wù)邏輯封裝在一個一個的函數(shù)里面牍汹,函數(shù)不會影響到參數(shù)列表(因為都是Copy),也不會影響到外部的狀態(tài)柬泽。

3.函數(shù)一等公民與高階函數(shù)

函數(shù)式編程中函數(shù)可以像變量一樣傳來傳去慎菲,可以作為參數(shù)也可以作為返回值,當一個函數(shù)接受函數(shù)參數(shù)是我們叫做高階函數(shù)锨并,swift中常用的有 Map露该,Reduce,F(xiàn)ilter等第煮。

4.柯里化

很多函數(shù)式編程的科普文章都寫了這個名次解幼,柯里化(Currying)是把接受多個參數(shù)的函數(shù)變換成接受一個單一參數(shù)(最初函數(shù)的第一個參數(shù))的函數(shù),并且返回接受余下的參數(shù)且返回結(jié)果的新函數(shù)的技術(shù),比如有一個函數(shù)需要兩個參數(shù)f(x包警,y)撵摆,柯里化之后就變成兩個函數(shù),一個接受x害晦,一個接受y特铝,最后的行為編程f(x)(y),這個東西是由于函數(shù)式編程吧函數(shù)作為一等公民,所以f(x)返回一個函數(shù),這個函數(shù)接受y為參數(shù)鲫剿,柯里化可以實現(xiàn)部分計算痒芝,但是有什么用,很多人在網(wǎng)上討論牵素。柯里化在工程中有什么好處? - 知乎

f(x,y) => 柯里化 f(x)(y)

5.純函數(shù)

函數(shù)式編程的一個重要概念就是純函數(shù)严衬,這種函數(shù)有兩個重要的標準。

1.在輸入一定的情況下輸出確定笆呆。

2.不會對函數(shù)外產(chǎn)生副作用请琳。

func add_pure(a: Int) -> Int {
        return a + 1
    }

上面的函數(shù)是純函數(shù),輸入確定之后輸出確定赠幕,并且不會改變外面變量的狀態(tài)

var b = 0;
func add_nopure(a: Int) -> Int {
    return a + b
}

上面的函數(shù)不是純函數(shù)俄精,因為返回值收到b的影響。

6.引用透明

引用透明和純函數(shù)概念差不多榕堰,引用透明導(dǎo)致一定的輸入與輸出是不會改變的竖慧,這樣可以方便編譯去做優(yōu)化。

7.遞歸

遞歸用于替代在命令式編程里面的循環(huán)逆屡,由于遞歸會導(dǎo)致棧益處圾旨,所以在函數(shù)式編程中,很多編譯器都會用尾遞歸調(diào)用來優(yōu)化魏蔗。尾調(diào)用優(yōu)化 - 阮一峰的網(wǎng)絡(luò)日志

下面我們用一個例子來說明命令式編程與函數(shù)式編程的區(qū)別
假如有一個學(xué)籍成績表單

enum GenderType {
    case boy
    case girl
}

struct Student{
    let name: String
    let gender: GenderType
    let source: Float
}

let students = [
        Student(name: "Make", gender: .boy, source: 75.0),
        Student(name: "Jason", gender: .boy, source: 80.0),
        Student(name: "Lucy", gender: .girl, source: 82.0),
        Student(name: "Lili", gender: .girl, source: 83.0),
        Student(name: "Amy", gender: .girl, source: 70.0),
        Student(name: "Jenny", gender: .girl, source: 72.0),
        Student(name: "Kelly", gender: .girl, source: 90.0),
        Student(name: "Helen", gender: .girl, source: 170.0),
    ]

我們現(xiàn)在有一個需求砍的,要獲取所有已女學(xué)生的成績姓名排名:
如果按照命令式的編程方式,
第一步:過濾所有女學(xué)生
第二部:排序所有女學(xué)生
第三部:吧所有女學(xué)生的名字放入數(shù)組返回

func getUpScoreName(studets : [Student] , type: GenderType)-> [String] {
        
       var genderStudent = [Student]()
       var genderName = [String]()

       for i in 0..<studets.count {
            if studets[i].gender == type {
                genderStudent.append(studets[i])
            }
        }
        
        for i in 0..<genderStudent.count {
            let stu = genderStudent[i]
            //2
            for j in stride(from: i, to: -1, by: -1){
                if stu.source < genderStudent[j].source {
                    genderStudent.remove(at: j + 1)
                    genderStudent.insert(stu, at: j)
                }
            }
        }
        
        for i in 0..<genderStudent.count {
            genderName.append(genderStudent[i].name)
        }
        
        return genderName
    }

可能代碼會是這樣的莺治。
如果我們用函數(shù)式編程的結(jié)果廓鞠,那可能這么去實現(xiàn):
讓Student實現(xiàn)對比接口:

extension Student : Comparable {
    static func ==(lhs: Student, rhs: Student) -> Bool {
        return false
    }
    
    static func < (lhs: Student, rhs: Student) -> Bool {
        return lhs.source < rhs.source
    }
}

獲取排序結(jié)果:

    func getUpScoreNameFC(studets : [Student] , type: GenderType) -> [String] {
        let names = studets.filter{ $0.gender == type }.sorted{ $0 < $1 }.map{ return $0.name }
        return names
    }

這樣的代碼看起來就是我需要什么,而不是我要一步一步的怎么去實現(xiàn)里面的東西谣旁,雖然Swift給我們提供的Map床佳,reduce等操作接口是系統(tǒng)API,但是我們自己寫方法的時候可以忘這方面靠近榄审,達到這種一看代碼就知道再干什么砌们,不需要一步一步去分析代碼是什么,一目了然的結(jié)果瘟判。

第二個例子是怎么消除局部可變變量怨绣,函數(shù)式編程要求變量不可變,而且不需要變量拷获,函數(shù)是純函數(shù)篮撑,這樣函數(shù)可以不受外部的影響。
加入我們現(xiàn)在要給班級里面的三個學(xué)生發(fā)小紅花:
如果是命令式編程方式:

  var count = 0
        mutating func flower() {
              getFlowerToStudent(studets: self.students, countTotal: 3)
        }
       mutating func getFlowerToStudent(studets : [Student] , countTotal: Int) {
        self.count = countTotal
        let max = UInt32(studets.count)
        while count > 0 {
            let index =  Int(arc4random() % max)
            count = count - 1
            print(studets[index])
        }
    }

我們首選申請一個局部變量匆瓜,記錄要給多少小朋友發(fā)紅花赢笨,然后在循環(huán)里面去減去這個變量未蝌,已判斷是否發(fā)送完畢。現(xiàn)在的邏輯還不復(fù)雜茧妒,但是如果邏輯很多的時候萧吠,又需要變量去維持狀態(tài)的時候,閱讀代碼就很困難桐筏,而且很容易因為狀態(tài)變量改變而出現(xiàn)Bug纸型。

如果是函數(shù)式編程可能就是這種結(jié)果:

  mutating func flower() {
        getFlowerToStudentFC(studets: self.students, countTotal: 3)
    }

    func getFlowerToStudentFC(studets : [Student], countTotal: Int) {
        if (countTotal == 0) {
            return
        }else {
            let index =  Int(arc4random() %  UInt32(studets.count))
            print(studets[index])
            self.getFlowerToStudentFC(studets: studets, countTotal: countTotal - 1)
        }
    }

沒有狀態(tài)變量,所有的函數(shù)都是純函數(shù)梅忌,這樣閱讀起來可以根據(jù)函數(shù)名稱很好的理解函數(shù)在干什么狰腌。

總結(jié):
swift不是純函數(shù)式編程的語言,但是再往函數(shù)式編程方向靠攏牧氮,我們在寫代碼的時候可以從我們底層的很model琼腔,或者viewmodel等簡單的模塊嘗試用函數(shù)式編程這種方式。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末踱葛,一起剝皮案震驚了整個濱河市丹莲,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌尸诽,老刑警劉巖甥材,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異逊谋,居然都是意外死亡擂达,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門胶滋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人悲敷,你說我怎么就攤上這事究恤。” “怎么了后德?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵部宿,是天一觀的道長。 經(jīng)常有香客問我瓢湃,道長理张,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任绵患,我火速辦了婚禮雾叭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘落蝙。我一直安慰自己织狐,他們只是感情好暂幼,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著移迫,像睡著了一般旺嬉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上厨埋,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天邪媳,我揣著相機與錄音,去河邊找鬼荡陷。 笑死雨效,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的亲善。 我是一名探鬼主播设易,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蛹头!你這毒婦竟也來了顿肺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤渣蜗,失蹤者是張志新(化名)和其女友劉穎屠尊,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體耕拷,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡讼昆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了骚烧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浸赫。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖赃绊,靈堂內(nèi)的尸體忽然破棺而出既峡,到底是詐尸還是另有隱情,我是刑警寧澤碧查,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布运敢,位于F島的核電站,受9級特大地震影響忠售,放射性物質(zhì)發(fā)生泄漏传惠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一稻扬、第九天 我趴在偏房一處隱蔽的房頂上張望卦方。 院中可真熱鬧,春花似錦腐螟、人聲如沸愿汰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽衬廷。三九已至摇予,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間吗跋,已是汗流浹背侧戴。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留跌宛,地道東北人酗宋。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像疆拘,于是被迫代替她去往敵國和親蜕猫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容