Swift 慣用語法

做者:Erica Sadun,原文連接,原文日期:2017-01-24
譯者:星夜暮晨;校對:Crystal Sun;定稿:CMBgit

長此以往,Swift 發展出一種別具一格的專用語法——即一組與其餘語言相差甚遠的基本慣用語法 (core idioms)。許多來自 Objective-C、Ruby、Java、Python 等等語言的開發者紛紛投向 Swift 的麾下。很多天前,Nicholas T Chambers 讓我幫他來磨練這門新習得的語言。他經過將 Ruby 代碼移植爲 Swift 的方式,來構建本身基本的編程技能。他所移植的代碼是這樣的:github

ruby
def find_common(collection)
    sorted = {}
    most = [0,0]

    for item in collection do
        if not sorted.key? item then
            sorted[item] = 0
        end

        sorted[item] += 1

        if most[1] < sorted[item] then
            most[0] = item
            most[1] = sorted[item]
        end
    end

    return most
end

而後他所嘗試改編而成的 Swift 代碼爲:編程

func find_common(items: [Int]) -> [Int] {
    var sorted = [Int: Int]()
    var most = [0, 0]

    for item in items {
        if sorted[item] == nil {
            sorted[item] = 0
        }

        sorted[item]! += 1

        if most[1] < sorted[item]! {
            most[0] = item
            most[1] = sorted[item]!
        }
    }

    return most
}

除了一些強制解包的代碼外,這二者之間幾乎沒有任何區別。我對 Ruby 並非瞭如指掌,可是這兩段代碼感受仍然仍是 C 語言的風格,而且也一點都不函數化(是函數式編程領域的意思,而不是「沒法工做」的意思)。swift

我知道 Ruby 支持相似 reduce 之類的操做,可是這裏咱們並無看到。當我剛開始學習 Swift 的時候,我作的第一件事情就是將大篇大篇的 Ruby 函數式調用方法用 Swift 實現出來。固然時至今日,仍然有不少諸如 selectrejectdelete_ifkeep_if 之類的功能仍然等着我用 Playground 實現出來,感受無窮無盡的樣子。不過講道理,這種作法很是適合學習 Swift。數組

下面就是經我建議以後重寫的版本:ruby

import Foundation

extension Array where Element: Hashable {
    /// Returns most popular member of the array
    ///
    /// - SeeAlso: https://en.wikipedia.org/wiki/Mode_(statistics)
    ///
    func mode() -> (item: Element?, count: Int) {
        let countedSet = NSCountedSet(array: self)
        let counts = countedSet.objectEnumerator()
            .map({ (item: $0 as? Element, count: countedSet.count(for: $0)) })
        return counts.reduce((item: nil, count: 0), {
            return ($0.count > $1.count) ? $0 : $1
        })
    }
    
}

就某些方面而言,這種重構顯然是做弊了,由於我「借用」了 NSCountedSet 的幫助,不過我以爲用 Swift 來編程並不意味着咱們必需要將 Foundation 拒之門外。在我看來,使用計數集 (counted set) 正是這段代碼的任務所在:「假設咱們有一個類型隨機的列表(儘管類型是相同的),列表當中的順序是隨機的。那麼該如何找到這個列表當中出現次數最多的元素呢?」。編程語言

下面是我關於重構的一些建議和想法:ide

  • 充分利用各類庫 (Leverage Libraries)。在遷移到 Swift 的時候,您須要考慮 Foundation 以及 Swift Foundation 類型是否會讓您的重構更加方便快捷。這裏計數集即是一個很好的例子,由於它自己就能夠自行完成所有的成員分組和計數。不過我但願可以擁有一個原生版本的計數集,這樣就不用操心於使人瘋狂的對象枚舉 (objectEnumerator),此外若是沒有指定可哈希元素的時候,代碼仍然能夠經過編譯的狀況了。函數式編程

  • 擁抱泛型 (Embrace Generics)。嘗試挑戰一下將示例中的列表換成隨機類型。將列表硬編碼 (hardcoding) 爲 Int 並非個好方法。所以,一旦您意識到要實現的功能須要用於多種類型的時候,請選擇將泛型引入您的解決方案中。函數

  • 必要時考慮使用協議 (Consider Protocols)。計數集的原生版本無論怎麼說也得是 Hashable 的,就如 Swift 原生的 Set 數據類型同樣。這裏我添加了一些限制條件,可是它編譯和運行的結果和咱們的預期並不一致。

  • 活用函數式編程 (Live Functionally)。全部相似「從列表中找尋某種類型的元素」的操做無不在明示着讓我去使用函數式編程來解決。若是在對列表進行迭代的時候,須要用變量來存儲中間狀態的話,那麼能夠考慮使用 Swift 最基本的函數式調用操做:map/reduce/filter,從而消除冗餘的顯式狀態 (explicit state)。

  • 避免使用全局函數 (Avoid Global Functions)。我以爲這段代碼最好是用集合擴展的方式進行實現,而不是使用獨立的函數。mode 函數基本上是對數組進行描述和操做的。這段代碼實現將做爲 Array 的一部分存在。我甚至以爲,我應該將其實現爲一個屬性,而不是一個函數,由於列表的 mode 功能應該是數組的本質所在 (intrinsic quality)。不過關於這一點,我還有些猶豫不定。

  • 別忘了編寫測試以及文檔 (Think Tests and Documents)。在編寫代碼以前就考慮如何編寫測試用例以及文檔,這已是 Swift 開發的核心所在。這裏我添加了一些相關的文本標記。不過我尚未添加相關的測試。

  • 使用良好的 Swift 語法規範 (Prefer Good Swiftsmanship)。首先,在我對代碼總體進行思考以前,我陷入了對語法細節的桎梏當中,好比說「使用條件綁定」以及「鍵入變量/儘量使用字面量」等等。不過隨着我花了大量的心思來思索以後,我對函數式編程的見解發生了改變,可是這並不意味着基本 Swift 語法規範就能夠被忽略掉。

世間有不少事情既須要顧全大局,也須要深刻細節 (A lot of this falls into the big picture little picture dichotomy)。在學習 Swift 的時候,您可能但願從細節開始學習:學習可空值的原理、學習如何正確的使用可空值、學習如何使用函數式編程等等,從而一直學習到如何建立測試、如何編寫文檔、如何利用協議和泛型。要學的東西實在是紛繁複雜,很難一步登天。

這些知識的基礎之上,又還有基本的 API 用法,這使得學習 Swift 變得更加困難。對於那些剛剛接觸蘋果開發的人而言,即使他們具有了現代編程語言的基礎知識,可是要區分出 Swift 原生類型和 Cocoa 類型的區別而且掌握 Cocoa/Cocoa Touch API 都將是一個重大的挑戰。

正因如此,用「更 Swift 化」的方式來編寫代碼,不只意味着要使用約定的慣用語法,同時還意味着要記住和使用語言所處平臺的相關特性。我但願計數集(以及其餘 Cocoa Foundation 當中沒被 Swift 原生化的類型)可以成爲 Swift 的原生部分。

本文由 SwiftGG 翻譯組翻譯,已經得到做者翻譯受權,最新文章請訪問 http://swift.gg

相關文章
相關標籤/搜索