閱讀本文須要對 Swift 基本語法和泛型有基本的瞭解。
本文約840字,閱讀大概須要 10 min。
收穫:對 Swift 中的泛型使用更加深刻。
複製代碼
假設,在項目中咱們須要實現一個 Stack 的數據結構,而且想支持任意基本類型。那麼,咱們可使用 Any 實現下面的代碼:objective-c
struct Stack {
var data = [Any]()
init(data: [Any]) {
self.data = data
}
mutating func pop() {
data.removeLast()
}
mutating func push(element: Any) {
data.append(element)
}
}
var stack = Stack(data: ["swift", "objective-c"])
stack.push(element: "iOS")
複製代碼
上述代碼能夠知足咱們的需求,它能夠支持任意類型。可是這種寫法在使用中仍是有不少缺點的:算法
print(stack.data.first as! String)
複製代碼
stack.push(element: 13)
//Error - Could not cast value of type 'Swift.Int' (0x10b4d7070) to 'Swift.String' (0x10b4d9190)
print(stack.data.last as! String)
複製代碼
因此,遇到此類需求時,咱們應該使用泛型來代替上面的 Any。swift
struct Stack<Element> {
var data = [Element]()
init(data: [Element]) {
self.data = data
}
mutating func pop() {
data.removeLast()
}
mutating func push(element: Element) {
data.append(element)
}
}
var stack = Stack(data: ["swift", "objective-c"])
stack.push(element: "iOS")
複製代碼
print(stack.data.first!)
複製代碼
// 編譯不成功 - Cannot convert value of type 'Int' to expected argument type 'String'
stack.push(element: 13)
複製代碼
接着上面的例子,假如咱們想在 Stack 的基礎上,添加一個計算棧內全部數字類型元素的和,咱們能夠用如下代碼實現:數組
extension Stack where Element: Numeric {
func sum() -> Element {
var total: Element = 0
for num in data {
total += num
}
return total
}
}
var numStack = Stack(data: [1, 2])
numStack.sum() // 3
// strStack 是沒法調用 sum 函數的,由於String 並無遵照 Numberic 協議
var strStack = Stack(data: ["Swift", "iOS13"])
複製代碼
extension Stack where Element: Numeric
的意思是指:只有 Stack 的元素遵照 Numberic 協議,才能調用 sum() 函數。這麼作是由於非數字類型是沒法進行累加操做的。若是咱們不添加該限制的話,total += num
這句代碼會報錯:Binary operator '+=' cannot be applied to two 'Element' operands
。bash
咱們能夠經過 泛型 + Extension 爲 Protocol 擴展兼容多類型的方法,好比標準庫提供的 map 函數,它的實現就是藉助 泛型 + Extension 實現的:數據結構
extension Collection {
func map<T>(_ transform: (Element) -> T) -> [T] {
var result = [T]()
result.reserveCapacity(self.count)
var position = startIndex
while position != endIndex {
result.append(transform(self[position]))
position = index(after: position)
}
return result
}
}
複製代碼
由於在調用 map() 函數時,咱們能夠肯定參數計算出 result 的大小,因此咱們可使用 result.reserveCapacity(self.count)
來優化代碼效率。關於 reserveCapacity
更加詳細的內容,請參見 Swift 中的 Array 性能比較: append vs reserveCapacity(譯)app
Fisher–Yates shuffle
算法是一個用來將一個有限集合生成一個隨機排列的算法(數組隨機排序)。dom
extension RandomAccessCollection where Self: MutableCollection {
mutating func shuffle() {
let n = count
guard n > 1 else { return }
for (i, pos) in indices.dropLast().enumerated() {
let otherPos = index(startIndex, offsetBy: Int.random(in: i..<n))
swapAt(pos, otherPos)
}
}
}
var arr = [1,2,3,4,5,6,7]
arr.shuffle()
//[4, 3, 5, 1, 6, 7, 2]
print(arr)
複製代碼