Swift閉包表達式
閉包表達式(Closure Expressions)
嵌套函數 是一個在較複雜函數中方便進行命名和定義自包含代碼模塊的方式。當然,有時候撰寫小巧的沒有完整定義和命名的類函數結構也是很有用處的,尤其是在您處理一些函數並需要將另外一些函數作爲該函數的參數時。
閉包表達式是一種利用簡潔語法構建內聯閉包的方式。 閉包表達式提供了一些語法優化,使得撰寫閉包變得簡單明瞭。 下面閉包表達式的例子通過使用幾次迭代展示了sort
函數定義和語法優化的方式。 每一次迭代都用更簡潔的方式描述了相同的功能。
sort 函數(The Sort Function)
Swift 標準庫提供了sort
函數,會根據您提供的基於輸出類型排序的閉包函數將已知類型數組中的值進行排序。 一旦排序完成,函數會返回一個與原數組大小相同的新數組,該數組中包含已經正確排序的同類型元素。
下面的閉包表達式示例使用sort
函數對一個String
類型的數組進行字母逆序排序,以下是初始數組值:
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
sort
函數需要傳入兩個參數:
- 已知類型的數組
- 閉包函數,該閉包函數需要傳入與數組類型相同的兩個值,並返回一個布爾類型值來告訴
sort
函數當排序結束後傳入的第一個參數排在第二個參數前面還是後面。如果第一個參數值出現在第二個參數值前面,排序閉包函數需要返回true
,反之返回false
。
該例子對一個String
類型的數組進行排序,因此排序閉包函數類型需爲(String, String) -> Bool
。
提供排序閉包函數的一種方式是撰寫一個符合其類型要求的普通函數,並將其作爲sort
函數的第二個參數傳入:
func backwards(s1: String, s2: String) -> Bool {
return s1 > s2
}
var reversed = sort(names, backwards)
// reversed 爲 ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
如果第一個字符串 (s1
) 大於第二個字符串 (s2
),backwards
函數返回true
,表示在新的數組中s1
應該出現在s2
前。 對於字符串中的字符來說,「大於」 表示 「按照字母順序較晚出現」。 這意味着字母"B"
大於字母"A"
,字符串"Tom"
大於字符串"Tim"
。 其將進行字母逆序排序,"Barry"
將會排在"Alex"
之後。
然而,這是一個相當冗長的方式,本質上只是寫了一個單表達式函數 (a > b)。 在下面的例子中,利用閉合表達式語法可以更好的構造一個內聯排序閉包。
閉包表達式語法(Closure Expression Syntax)
閉包表達式語法有如下一般形式:
{ (parameters) -> returnType in
statements
}
閉包表達式語法可以使用常量、變量和inout
類型作爲參數,不提供默認值。 也可以在參數列表的最後使用可變參數。 元組也可以作爲參數和返回值。
下面的例子展示了之前backwards
函數對應的閉包表達式版本的代碼:
reversed = sort(names, { (s1: String, s2: String) -> Bool in
return s1 > s2
})
需要注意的是內聯閉包參數和返回值類型聲明與backwards
函數類型聲明相同。 在這兩種方式中,都寫成了(s1: String, s2: String) -> Bool
。 然而在內聯閉包表達式中,函數和返回值類型都寫在大括號內,而不是大括號外。
閉包的函數體部分由關鍵字in
引入。 該關鍵字表示閉包的參數和返回值類型定義已經完成,閉包函數體即將開始。
因爲這個閉包的函數體部分如此短以至於可以將其改寫成一行代碼:
reversed = sort(names, { (s1: String, s2: String) -> Bool in return s1 > s2 } )
這說明sort
函數的整體調用保持不變,一對圓括號仍然包裹住了函數中整個參數集合。而其中一個參數現在變成了內聯閉包(相比於backwards
版本的代碼)。
根據上下文推斷類型(Inferring Type From Context)
因爲排序閉包函數是作爲sort
函數的參數進行傳入的,Swift可以推斷其參數和返回值的類型。 sort
期望第二個參數是類型爲(String, String) -> Bool
的函數,因此實際上String
,String
和Bool
類型並不需要作爲閉包表達式定義中的一部分。 因爲所有的類型都可以被正確推斷,返回箭頭 (->
) 和圍繞在參數周圍的括號也可以被省略:
reversed = sort(names, { s1, s2 in return s1 > s2 } )
實際上任何情況下,通過內聯閉包表達式構造的閉包作爲參數傳遞給函數時,都可以推斷出閉包的參數和返回值類型,這意味着您幾乎不需要利用完整格式構造任何內聯閉包。
單表達式閉包隱式返回(Implicit Return From Single-Expression Clossures)
單行表達式閉包可以通過隱藏return
關鍵字來隱式返回單行表達式的結果,如上版本的例子可以改寫爲:
reversed = sort(names, { s1, s2 in s1 > s2 } )
在這個例子中,sort
函數的第二個參數函數類型明確了閉包必須返回一個Bool
類型值。 因爲閉包函數體只包含了一個單一表達式 (s1 > s2
),該表達式返回Bool
類型值,因此這裏沒有歧義,return
關鍵字可以省略。
參數名稱縮寫(Shorthand Argument Names)
Swift 自動爲內聯函數提供了參數名稱縮寫功能,您可以直接通過$0
,$1
,$2
來順序調用閉包的參數。
如果您在閉包表達式中使用參數名稱縮寫,您可以在閉包參數列表中省略對其的定義,並且對應參數名稱縮寫的類型會通過函數類型進行推斷。 in
關鍵字也同樣可以被省略,因爲此時閉包表達式完全由閉包函數體構成:
reversed = sort(names, { $0 > $1 } )
在這個例子中,$0
和$1
表示閉包中第一個和第二個String
類型的參數。
運算符函數(Operator Functions)
實際上還有一種更簡短的方式來撰寫上面例子中的閉包表達式。 Swift 的String
類型定義了關於大於號 (>
) 的字符串實現,其作爲一個函數接受兩個String
類型的參數並返回Bool
類型的值。 而這正好與sort
函數的第二個參數需要的函數類型相符合。 因此,您可以簡單地傳遞一個大於號,Swift可以自動推斷出您想使用大於號的字符串函數實現:
reversed = sort(names, >)
更多關於運算符表達式的內容請查看 運算符函數。