Swift 泛型糙及,將復(fù)雜計算運用于數(shù)組

1详幽、假如我們需要寫一個函數(shù),它接受一個給定的整型數(shù)組,通過計算得到并返回一個新數(shù)組唇聘,新數(shù)組各項為原數(shù)組中對應(yīng)的整型數(shù)據(jù)加一版姑。這個簡單的例子僅僅需要使用一個for循環(huán)就能解決,實現(xiàn)如下:

private func incrementArray(xs: [Int]) -> [Int] {
    var result: [Int] = []
    for x in xs {
        result.append(x + 1)
    }
    return result  
}

2迟郎、現(xiàn)在假設(shè)我們還需要一個函數(shù)剥险,用于生成一一個每項都為參數(shù)數(shù)組對應(yīng)項兩倍的新數(shù)組。這同樣能很容易使用一個for循環(huán)實現(xiàn):

private func doubleArray(xs: [Int]) -> [Int] {
    var result: [Int] = []
    for x in xs {
        result.append(x * 2)
    }
    return result  
}

3宪肖、這兩個函數(shù)有大量相同的代碼表制,我們能不能將沒有區(qū)別的地方抽象出來,并單獨寫一個體現(xiàn)這種模式且更通用的函數(shù)呢控乾?像這樣的函數(shù)需要追加一個新參數(shù)來接受一個函數(shù)么介,這個參數(shù)能根據(jù)各個數(shù)組項計算得到新的整型數(shù)值:

private func computeIntArray(xs: [Int], transform: (Int) -> Int) -> [Int] {
    var result: [Int] = []
    for x in xs {
        result.append(transform(x))
    }
    return result
}

4、現(xiàn)在蜕衡,取決于我們想如何根據(jù)原數(shù)組得到一個新數(shù)組壤短,我們可以向函數(shù)傳遞不同的參數(shù)來實現(xiàn),如下

let array = [1, 2, 3, 4, 5]
print(computeIntArray(xs: array, transform: { x in
    x * 2
}))

5慨仿、代碼仍然不像想象中的那么靈活久脯,假如我們想要得到一個布爾類型的新數(shù)組,用于表示原數(shù)組中對應(yīng)的數(shù)字是否是偶數(shù)镰吆,我們可以嘗試編寫一些像下面這樣的代碼:

private func isEvenArray(xs: [Int]) -> [Bool] {
    computeIntArray(xs: xs) { x in
        x % 2 == 0
    }
}

不幸的是帘撰,這段代碼導(dǎo)致了一個類型錯誤。問題在于我們的 computeIntArray 函數(shù)接受一個 (Int) -> Int 類型的參數(shù)万皿,也就是說該參數(shù)是一個返回整型值的參數(shù)摧找。而在 isEvenArray 函數(shù)的定義中,我們傳遞了一個 (Int) -> Bool 類型的參數(shù)相寇,于是導(dǎo)致了類型錯誤慰于。

6钮科、我們該如何解決這個問題呢唤衫?一種最普通的方案是定義新版本的 computeBoolArray 函數(shù),接受一個 (Int) -> Bool 類型的參數(shù)绵脯,在這里的實現(xiàn)我就不寫了佳励,但是這個方案的擴展性并不好。如果接下來我們需要計算 String 類型呢蛆挫?是否還需要定義一個高階函數(shù)來接受 (Int) -> String 類型的參數(shù)赃承?

幸運的是,該問題有一個解決方案:我們可以使用 泛型 悴侵。computeIntArraycomputeBoolArray 的定義是相同的瞧剖,唯一的區(qū)別在于類型簽名 (type signature) 。假如我們定義一個相似的函數(shù) computeStringArray 來支持 String 類型,其函數(shù)體將會與先前兩個函數(shù)完全一致抓于。事實上做粤,相同部分的代碼可以用于 任何 類型。我們真正想做的是寫一個能夠適用于每種可能類型的泛型函數(shù)

private func genericComputeArray<T>(xs: [Int], transform: (Int) -> T) -> [T] {
    var result: [T] = []
    for x in xs {
        result.append(transform(x))
    }
    return result
}

關(guān)于這段代碼捉撮,最有意思的是他的類型簽名怕品。理解這個類型簽名有助于你將 genericComputeArray<T> 理解為一個函數(shù)族。類型參數(shù) T 的每個選擇都會確定一個新函數(shù)巾遭。該函數(shù)接受一個整型數(shù)組和一個 Int -> T 類型的函數(shù)作為參數(shù)肉康,并返回一個 [T] 類型的數(shù)組。

7灼舍、我們?nèi)阅苓M一步將這個函數(shù)一般化吼和。沒有理由讓它僅能對類型為 [Int] 的輸入數(shù)組進行處理。將數(shù)組類型進行抽象片仿,能得到下面這樣的類型簽名:

private func map<Element, T>(xs:[Element], transform: (Element) -> T) -> [T] {
    var result: [T] = []
    for x in xs {
        result.append(transform(x))
    }
    return result
}

這里我們寫了一個 map 函數(shù)纹安,它在兩個維度都是通用的:對于任何 Element 的數(shù)組和 transform: (Element) -> T 函數(shù),它都會生成一個 T 的新數(shù)組砂豌。這個 map 函數(shù)甚至比我們之前看到的 genericComputeArray 函數(shù)更通用厢岂。事實上,我們可以通過 map 來定義 genericComputeArray:

private func genericComputeArray<T>(xs: [Int], transform: (Int) -> T) -> [T] {
    return map(xs: xs, transform: transform)
}

同樣的阳距,上述函數(shù)的定義并沒有什么太過特別之處:函數(shù)接受 xstransform 兩個參數(shù)之后塔粒,將它們傳遞給 map 函數(shù),然后返回結(jié)果筐摘。關(guān)于這個定義卒茬,最有意思非類型莫屬。 genericComputeArray(_: transform:)map 函數(shù)的一個實例咖熟,只是它有一個更具體的類型圃酵。實際上,比起定義一個頂層 map 函數(shù)馍管,按照 Swift 的慣例將 map 定義為 Array 的擴展會更合適:

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

我們在函數(shù)的 transform 參數(shù)中所使用的 Element 類型源自于 SwiftArray 中對 Element 所進行的泛型定義郭赐。

作為 map(xs, transform) 的替代,我們現(xiàn)在可以通過 xs.map(transform) 來調(diào)用 Arraymap 函數(shù):

private func genericComputeArray<T>(xs: [Int], transform: (Int) -> T) -> [T] {
    return xs.map(transform)
}

8确沸、想必你會很樂意聽到其實并不需要自己像這樣來定義 map 函數(shù)捌锭,因為它已經(jīng)是 Swift 標準庫的一部分了(實際上,它基于 SequenceType 協(xié)議被定義)罗捎。本文章的重點并不是說你應(yīng)該自己定義 map ;我們只是想要告訴你 map 的定義中并沒有什么復(fù)雜難懂的魔法--你能夠輕松地自己定義它观谦!。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末桨菜,一起剝皮案震驚了整個濱河市豁状,隨后出現(xiàn)的幾起案子捉偏,更是在濱河造成了極大的恐慌,老刑警劉巖泻红,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件告私,死亡現(xiàn)場離奇詭異,居然都是意外死亡承桥,警方通過查閱死者的電腦和手機驻粟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來凶异,“玉大人蜀撑,你說我怎么就攤上這事∈1颍” “怎么了酷麦?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長喉恋。 經(jīng)常有香客問我沃饶,道長,這世上最難降的妖魔是什么轻黑? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任糊肤,我火速辦了婚禮,結(jié)果婚禮上氓鄙,老公的妹妹穿的比我還像新娘馆揉。我一直安慰自己,他們只是感情好抖拦,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布升酣。 她就那樣靜靜地躺著,像睡著了一般态罪。 火紅的嫁衣襯著肌膚如雪噩茄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天复颈,我揣著相機與錄音绩聘,去河邊找鬼。 笑死券膀,一個胖子當著我的面吹牛君纫,可吹牛的內(nèi)容都是我干的驯遇。 我是一名探鬼主播芹彬,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼叉庐!你這毒婦竟也來了舒帮?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎玩郊,沒想到半個月后肢执,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡译红,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年预茄,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侦厚。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡耻陕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出刨沦,到底是詐尸還是另有隱情诗宣,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布想诅,位于F島的核電站召庞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏来破。R本人自食惡果不足惜篮灼,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望徘禁。 院中可真熱鬧穿稳,春花似錦、人聲如沸晌坤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽骤菠。三九已至它改,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間商乎,已是汗流浹背央拖。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鹉戚,地道東北人鲜戒。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像抹凳,于是被迫代替她去往敵國和親遏餐。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

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