在幕後看看Swift中的Map,Filter和Reduce的實現

一個函數接受一些輸入,對它作一些事情並建立一個輸出。功能有簽名和正文。若是爲函數提供相同的輸入,則始終得到相同的輸出。簡而言之,這是函數的定義。git

如今咱們將經過仔細研究它們來討論更多功能。咱們將在Swift中探索更高階的函數。將另外一個函數做爲輸入或返回函數的函數稱爲高階函數。編程

在Swift中,咱們使用Map,Filter,Reduce。當咱們使用這些功能時,它彷佛很神奇。此時,您可能不知道幕後發生了什麼。經過函數式編程的思想和方法來映射,過濾和減小工做。即便Swift不是一種純粹的功能語言,它也可讓你作功能性的東西。swift

如今讓咱們一個接一個地看一下背景中發生的事情。首先,咱們將爲某些特定數據類型實現這些函數的基本版本,而後咱們將嘗試實現通用版本。 ####Map 函數 假設咱們有一個整數數組,咱們須要編寫一個函數,在向原始數組的每一個元素添加一些delta值以後返回一個新數組。咱們可使用以下的簡單for循環輕鬆地爲此編寫函數:數組

func increment(by delta: Int, to array: [Int]) -> [Int] {
    var result: [Int] = []
    for element in array {
        result.append(element + delta)
    }
    return result
}

increment(by: 2, to: arr) //[4, 5, 6, 7]
複製代碼

如今咱們須要另外一個函數,它經過將原始數組的每一個元素加倍來返回一個新數組。爲此,咱們能夠像下面這樣實現它:bash

var arr = [2, 3, 4, 5]

func square(_ array: [Int]) -> [Int]{
    var result: [Int] = []
    for element in array {
        result.append(element * element)
    }
    return result
}

square(arr) //[4, 9, 16, 25]
複製代碼

若是咱們看一下上面這兩個函數,咱們就能夠發現它們基本上都在作一樣的事情。只有for循環內部的功能不一樣。它們都將Integer數組做爲輸入,使用for循環轉換每一個元素,並返回一個新數組。因此基本上主要是將每一個元素轉換爲新的元素。app

因爲Swift支持高階函數,咱們能夠編寫一個函數,它將獲取一個整數數組,將函數轉換爲輸入,並經過將transform函數應用於原始數組的每一個元素來返回一個新數組。函數式編程

var arr = [2, 3, 4, 5]

func apply(for array: [Int], transform: (Int) -> Int) -> [Int] {
    var result: [Int] = []
    for element in array {
        result.append(transform(element))
    }
    return result
}

apply(for: arr) { $0 * 3 } //[6, 9, 12, 15]
複製代碼

但仍然存在以上問題:它只返回一個整數數組。例如,若是咱們要求將輸入整數數組轉換爲字符串數組,那麼咱們就不能用這個函數作到這一點。爲此,咱們須要編寫適用於任何類型的通用函數。函數

func genericApply<T>(to array: [Int], transform: (Int) -> T) -> [T] {
    var result: [T] = []
    for element in array {
        result.append(transform(element))
    }
    return result
}

genericApply(to: arr) { String($0) } //["2", "3", "4", "5"]
複製代碼

咱們能夠在Array擴展中實現泛型函數,以下所示:ui

  1. 在Array Extension中聲明一個map函數,它使用泛型類型T.
  2. 該函數採用類型(元素) - > T的函數做爲輸入
  3. 聲明一個空結果數組,該數組在函數內部保存T類型的數據。
  4. 實現for循環迭代自身並調用transform函數將元素轉換爲T類型
  5. 將轉換後的值附加到結果數組中
var array = ["boudhayan", "biswas"]

extension Array {
    func map<T>(_ transform: (Element) -> T) -> [T] {
        var result: [T] = []
        for element in self {
            result.append(transform(element))
        }
        return result
    }
}

array.map { $0.count }  //[9, 6]
複製代碼

這就是Map函數在Swift中的工做方式。若是咱們須要實現map函數,那麼咱們將像上面同樣實現它。因此基本上,它不會在數組中產生任何魔法 - 咱們能夠很容易地本身定義函數。 ##Filter函數 假設咱們有一個整數數組,咱們只想在數組中保留偶數。咱們能夠經過使用簡單的lo來實現這一點。spa

let integers = [1, 2, 3, 4, 5, 6]

func evenIntegers(from array: [Int]) -> [Int] {
    var result: [Int] = []
    for element in array where element % 2 == 0 {
        result.append(element)
    }
    return result
}

evenIntegers(from: integers) //[2, 4 6]
複製代碼

再一次,咱們有一個表示項目類文件名的字符串數組,咱們只想保留.swift文件。這也能夠經過如下單循環完成:

let projectFiles = ["classA.swift", "classB.swift", ".gitignore", "ReadMe.md"]

func swiftFiles(from array: [String]) -> [String] {
    var result: [String] = []
    for element in array where element.hasSuffix(".swift") {
        result.append(element)
    }
    return result
}

swiftFiles(from: projectFiles) //["classA.swift", "classB.swift"]
複製代碼

若是咱們仔細研究上述兩個函數的實現,那麼咱們就能夠理解它們基本上作一樣的事情 - 只有兩個數組的數據類型不一樣。咱們能夠經過實現泛型過濾器函數來歸納這一點,該函數將數組和函數做爲輸入,而且根據includeElement函數的輸出,它決定是否在結果數組中添加元素。

extension Array {
    func filter(_ includeElement: (Element) -> Bool) -> [Element] {
        var result: [Element] = []
        for element in self where includeElement(element) {
            result.append(element)
        }
        return result
    }
}
複製代碼

##Reduce函數 假設咱們有一個整數數組,咱們想要實現兩個返回sum和元素乘積的函數。咱們能夠經過使用一個簡單的for循環來實現它:

func sum(_ integers: [Int]) -> Int {
    var result = 0
    for element in integers {
        result += element
    }
    return result
}

func product(_ integers: [Int]) -> Int {
    var result = 1
    for element in integers {
        result *= element
    }
     return result
}

sum([1, 2, 3]) //6
product([2, 3, 4]) //24
複製代碼

如今不是擁有一個整數數組,而是說咱們有一個字符串數組,咱們想要鏈接數組中的全部元素:

var names = ["Boudhayan", "Biswas"]

func concatenate(_ strings: [String]) -> String {
    var result = ""
    for element in strings {
        result += element
    }
    return result
}

concatenate(names) //BoudhayanBiswas
複製代碼

這三個功能基本上都是同樣的。它們將數組做爲輸入,初始化結果變量,迭代數組,並更新結果變量。

從這裏咱們能夠實現一個適用於全部人的通用函數。爲此,咱們須要結果變量的初始值和在每次迭代中更新該變量的函數。

因此咱們能夠用如下定義實現泛型函數:

上述實現對於[Element]類型的任何輸入數組都是通用的。它將計算類型T的結果。要工做,它須要類型T的初始值以分配給結果變量。而後,它須要一個類型(T,元素) - > T的函數,它將在每次迭代中用於for循環內部以更新結果變量。 #感謝閱讀!

相關文章
相關標籤/搜索