函數(shù)式思想--Swift中Map、Filter褥紫、Reduce函數(shù)實現(xiàn)原理及仿寫

對于絕大多數(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末侦铜,一起剝皮案震驚了整個濱河市专甩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌钉稍,老刑警劉巖涤躲,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異贡未,居然都是意外死亡种樱,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門羞秤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缸托,“玉大人,你說我怎么就攤上這事瘾蛋±洌” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵哺哼,是天一觀的道長佩抹。 經(jīng)常有香客問我叼风,道長,這世上最難降的妖魔是什么棍苹? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任无宿,我火速辦了婚禮,結(jié)果婚禮上枢里,老公的妹妹穿的比我還像新娘孽鸡。我一直安慰自己,他們只是感情好栏豺,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布彬碱。 她就那樣靜靜地躺著,像睡著了一般奥洼。 火紅的嫁衣襯著肌膚如雪巷疼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天灵奖,我揣著相機與錄音嚼沿,去河邊找鬼。 笑死瓷患,一個胖子當著我的面吹牛骡尽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播尉尾,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼爆阶,長吁一口氣:“原來是場噩夢啊……” “哼燥透!你這毒婦竟也來了沙咏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤班套,失蹤者是張志新(化名)和其女友劉穎肢藐,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吱韭,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡吆豹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了理盆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片痘煤。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖猿规,靈堂內(nèi)的尸體忽然破棺而出衷快,到底是詐尸還是另有隱情,我是刑警寧澤姨俩,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布蘸拔,位于F島的核電站师郑,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏调窍。R本人自食惡果不足惜宝冕,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望邓萨。 院中可真熱鬧地梨,春花似錦、人聲如沸缔恳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽褐耳。三九已至诈闺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間铃芦,已是汗流浹背雅镊。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留刃滓,地道東北人仁烹。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像咧虎,于是被迫代替她去往敵國和親卓缰。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

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