阅读本文须要对 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)
复制代码