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

敲鐘人Quasimodo發表於2019-02-21

一個函式接受一些輸入,對它做一些事情並建立一個輸出。功能有簽名和正文。如果為函式提供相同的輸入,則始終獲得相同的輸出。簡而言之,這是函式的定義。

現在我們將通過仔細研究它們來討論更多功能。我們將在Swift中探索更高階的函式。將另一個函式作為輸入或返回函式的函式稱為高階函式。

在Swift中,我們使用Map,Filter,Reduce。當我們使用這些功能時,它似乎很神奇。此時,您可能不知道幕後發生了什麼。通過函數語言程式設計的思想和方法來對映,過濾和減少工作。即使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]
複製程式碼

現在我們需要另一個函式,它通過將原始陣列的每個元素加倍來返回一個新陣列。為此,我們可以像下面這樣實現它:

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迴圈轉換每個元素,並返回一個新陣列。所以基本上主要是將每個元素轉換為新的元素。

由於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擴充套件中實現泛型函式,如下所示:

  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來實現這一點。

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迴圈內部以更新結果變數。
#感謝閱讀!

相關文章