swift是面向协议编程,果然名不虚传
IteratorProtocol 协议
public protocol IteratorProtocol<Element> {
associatedtype Element
mutating func next() -> Element?
}
这样所有遵守了IteratorProtocol协议的类型,都是可以使用next方法的,这已经很完美了。但是!迭代器只能消费一次, 这里举一个不恰当的例子:
let numbers = [10, 20, 30]
// 从序列要一个迭代器(IteratorProtocol)
var it = numbers.makeIterator()
// 一步一步消费
print(it.next() as Any) // Optional(10)
print(it.next() as Any) // Optional(20)
print(it.next() as Any) // Optional(30)
print(it.next() as Any) // nil —— 已经到头了
// 同一个 it 再 next,永远是 nil(状态已经走到结束)
print(it.next() as Any) // nil
print(it.next() as Any) // nil
想再从头遍历一遍,不能指望复活这个it,只能再向序列要一个新的迭代器:
var it2 = numbers.makeIterator()
print(it2.next() as Any) // Optional(10) —— 又从第一个开始
但是这里的makeIterator是sequence协议要求提供的东西,之所以说这个例子不恰当,是因为我似乎在用已经解决的问题去回答问题,这里不应该把sequence牵涉进来。
那么,接下来的例子将非常合适。
struct CountFromTo: IteratorProtocol {
var current: Int
let end: Int
init(from: Int, through: Int) {
current = from; end = through
}
mutating func next() -> Int? {
guard current <= end else { return nil }
defer { current += 1 }
return current
}
}
var it = CountFromTo(from: 3, through: 5)
while let x = it.next() { print(x) } // 耗尽
print(it.next()) //nil,因为之前已经耗尽了
// 不能复活it,只能再来一个新的迭代器实例
var it2 = CountFromTo(from: 3, through: 5)
print(it2.next() as Any) // 又从 3 开始
var it = CountFromTo(from: 3, through: 5)
现在假设我们是swift标准库团队开发人员,要实现Array,我们需要提供给开发者类似以下这些功能
for x in arrarr.map { }、arr.filter { }的功能和别的“能挨个读一遍某个东西”的方法用同一套API
下标 arr[i]可以实现“挨个读一遍”的功能,但是正如我们提到的for x in arr / arr.map这种功能,它们只想对每个元素做某事,不需要关心下标。
for x in arr {
print(x)
}
//可以通过这种方式实现
var __iterator = arr.获取iterator()
while let x = __iterator.next() {
print(x)
}
map大致如下
func mapSimple<T>(_ transform: (Element) -> T) -> [T] {
var result: [T] = []
var it = 获取iterator()
while let x = it.next() {
result.append(transform(x))
}
return result
}
可以看出不论实现哪个功能都需要array有一个获取iterator的方法,给这个方法起名叫做makeIterator,也就是说array既要有next方法,又要有makeiterator的方法,我们把这两个方法都放入一个起名为sequence的protocol中,这就是sequence的由来了。Sequence 是 Swift 中最轻量的遍历协议。一个类型只要遵守 Sequence,就能用 for-in 遍历。实现了 Sequence的结构体或类 必须关联一个遵守 IteratorProtocol 的类型,
-
Sequence是工厂:生产迭代器 -
IteratorProtocol是产品:实际遍历逻辑
所以不能说实现了Sequence就是是实现了IteratorProtocol.
仅仅实现Sequence协议,你的类型就能享受所有Sequence的默认extension方法:map、filter、reduce、contains(Element: Equatable)、reversed。
//Sequence 协议:
protocol Sequence<Element> {
associatedtype Element where Self.Element == Self.Iterator.Element
associatedtype Iterator: IteratorProtocol
func makeIterator() -> Iterator
}
Sequence 够用了吗?
Sequence 只保证:能 makeIterator(),按顺序 next() 一个个拿。
适合:for-in、map、filter 等扫一遍的事。
但日常还会遇到:
-
第 3 个元素是谁?(随机访问某一位)
-
有多少个?(count)
-
第一个、最后一个下标怎么表示?
只靠 Iterator:只能往后走,不能跳到中间,也不一定有常数时间的长度概念(有些序列是无限的、或算长度很贵)。
所以要在 Sequence 上再叠一层:能按下标(或索引)访问、有明确首尾——这就是 Collection 的由来。
Collection 在解决什么?
在能遍历之上,再约定像容器一样用下标访问的能力。典型能力包括(概念上):
-
有
startIndex/endIndex -
能用
collection[index]读元素(subscript) -
索引可以
index(after:)往后走(不一定只是Int + 1,字符串的Index就复杂) -
往往还能提供
count(有的集合是 O(n) 算出来)
public protocol Collection: Sequence {
associatedtype Index: Comparable
var startIndex: Index { get }
var endIndex: Index { get }
subscript(position: Index) -> Element { get }
func index(after i: Index) -> Index
}
`Collection` 协议**继承自** `Sequence` 协议,因此任何遵守 `Collection` 的类型**自动满足** `Sequence` 的所有要求。
Array 是最典型的 Collection:下标是 Int,从 0 到 count-1。
Sequence ←── 更基础:只保证能遍历
↑
Collection ←── 继承 Sequence,并加:索引 + 下标访问 + …
在 Swift 里遵守 Collection 只能说明它是可按索引访问的一段序列,不一定是自己拥有一块独立存储的容器。例如Range,遵守RandomAccessCollection属于 Collection 一族
let r = 0..<10
print(r.count) // 10
print(r[r.startIndex]) // 0
这里并没有一个数组在内存里存 0,1,2,...,9;Range 只是用起点、终点描述区间,按需算出元素。它更像区间视图,不是传统意义上的数组那种容器。
本文使用 文章同步助手 同步