做者: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 實現出來。固然時至今日,仍然有不少諸如 select
、reject
、delete_if
、keep_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。