使用 Key Paths 創(chuàng)建自定義查詢函數(shù)
本文轉載自微信公眾號「Swift社區(qū)」,作者Swift社區(qū)。轉載本文請聯(lián)系Swift社區(qū)公眾號。
前言
作為一個相當嚴格,靜態(tài)編譯的語言,Swift 可能不會在語法自定義方面提供許多渠道,但這實際上確正好相反。通過如何在 Swift 中自定義操作符,Swift 中 key paths 的能力,函數(shù)/結果構建器 等功能,我們有很多機會為特定用例進行調整 Swift 的語法。
當然,無可爭議的是,任何類型的語法定制都應小心謹慎地,因為如果我們不小心,非標準語法也可能很容易成為混亂的源泉。但是,在某些情況下,權衡可能是值得的,并且可以易于讓我們制作類似 DSL 這種可以幫助我們使代碼更清晰的語法。
否定布爾值的 key pahts
讓我們查看一個這樣的案例,說我們正在研究一個應用程序,用于管理,過濾和排序文章,其中包含以下 Article 數(shù)據(jù)模型:
- struct Article {
- var title: String
- var body: String
- var category: Category
- var isRead: Bool
- ...
- }
現(xiàn)在讓我們看一下我們的代碼庫中的一個非常常見的任務是過濾各種集合,每個集合包含上述模型的實例。這樣做的一種方法是利用任何 "Swift key paths 表達式可以自動轉換為函數(shù)" 的功能,這讓我們在過濾任何布爾屬性時, 可以使用如下在篩選 isread 時的凝練的語法:
- let articles: [Article] = ...
- let readArticles = articles.filter(\.isRead)
這真的是非常好,但是,只有在我們想要與 true 比較時才能使用以上語法 ——如果我們想創(chuàng)建包含所有未讀文章的類似過濾的數(shù)組,那么我們必須使用閉包(或 傳入一個函數(shù)[1])代替:
- let unreadArticles = articles.filter { !$0.isRead }
這肯定不是一個大問題,但如果上述操作是我們在代碼上的許多不同地方上演的東西,那么我們可能會開始問自己:“如果我們也可以使用否定的布爾值的 key paths 語法會不會更好?“
這就是語法自定義的概念進來的地方。通過實現(xiàn)以下前綴函數(shù),我們實際上可以創(chuàng)建一個小小的調整,這將讓我們不用擔心 true 或 false 的使用 key paths:
- prefix func !<T>(keyPath: KeyPath<T, Bool>) -> (T) -> Bool {
- return { !$0[keyPath: keyPath] }
- }
以上基本上就是是重載內置的 ! 前置操作符,讓其可以應用于任何 Bool key paths,以便將其轉換為否定(或翻轉)其值的函數(shù) ——現(xiàn)在我們可以計算我們的 UnreadArticles 數(shù)組了:
- let unreadArticles = articles.filter(!\.isRead)
基于 key paths 的比較
現(xiàn)在,進一步采取措施,讓我們也可以使用 key paths 來形成篩選器查詢,該篩選器查詢將給定屬性與任何 Equatable 的值進行比較。例如,如果我們想要根據(jù)每篇文章的類別過濾我們的文章類別,那將變得有用。該屬性,類別的類型目前被定義為如下所示的枚舉:
- extension Article {
- enum Category {
- case fullLength
- case quickReads
- case basics
- ...
- }
- }
就像我們之前重載的 ! 操作符一樣,我們也可以用 == 運算符進行同樣的事情,我們將返回一個返回 Bool 的閉包,然后可以直接傳遞給篩選器(如 filter 過濾器):
- func ==<T, V: Equatable>(lhs: KeyPath<T, V>, rhs: V) -> (T) -> Bool {
- return { $0[keyPath: lhs] == rhs }
- }
通過以上重載,我們現(xiàn)在可以使用基于 key paths 的比較輕松過濾任何集合,如下所示:
- let fullLengthArticles = articles.filter(\.category == .fullLength)
結語
Swift 讓我們通過幾個輕量級重載輕松創(chuàng)建上述功能的事實是非常棒的或令人難以置信的。我傾向于在中間的某個地方停下,認為我們確實可以讓部分 Swift 的語法調整為適合我們的編寫,但同時,我認為應該始終盯緊我們使 diam 更簡單的目標來調整這些代碼。
參考資料
[1]傳入一個函數(shù): https://www.swiftbysundell.com/articles/first-class-functions-in-swift/