Sequences
Sequence
协议位于系统架构的底层,其定义是
1
2
3
4
5
6protocol Sequence {
associatedtype Iterator: IteratorProtocol
func makeIterator()-> Iterator
}
Iterators
1
2
3
4
5
protocol IteratorProtocol{
associatedtype Element
mutating func next()-> Element?
}
实现了IteratorProtocol协议就可以遍历实例中的数据了,比如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36struct PrefixIterator:IteratorProtocol {
let string:String
var offset:String.Index
init(string:String) {
self.string = string
offset = string.startIndex
}
typealias Element = String // 这里可以省略,因为编译器可以从返回值的类型中推断出来
mutating func next() -> String? {
guard offset < string.endIndex else { return nil }
offset = string.index(after: offset)
let offSetString = string[string.startIndex..<offset]
return offSetString;
}
}
var prefixIterator = PrefixIterator.init(string: "Hello")
while let content = prefixIterator.next() {
print(content)
}
struct PrefixSequence:Sequence {
let string: String
func makeIterator() -> PrefixIterator {
return PrefixIterator(string:string)
}
}
for prefix in PrefixSequence(string:"Hello"){
print("for in\(prefix)")
}
PrefixSequence(string:"Hello").map {$0.uppercased()}
如上所示,我么要想对一个Type实现for in 操作,需要两步:
- 创建一个遍历器:让其遵守
IteratorProtocol
协议 - 创建一个Type:让其遵守
Sequence
协议,在实现makeIterator
方法时将第一步中的Iterator
返回即可Iterator
有两种不同的语义,值语义(将要遍历的实例拷贝)和引用语义(不拷贝所遍历的实例),比如
1 | let sequ = stride(from: 0, to: 10, by: 1) |
Sequence的闭包是懒执行的,只有到获取某个数值的时候才执行,比如
Array(fibsSequence2.prefix(10))
这才让构造方法fibsSequence2.prefix(10)
产生作用,如果Sequence
提前计算了其数值,因为其实无穷Sequence
,所以当越界的时候其就会崩溃。
有些Sequence是不稳定的,也就是说每次的遍历,结果可能不一样,比如网络流,磁盘文件,UI事件流,以及其它类型的数据,它们都可以被建模,作为Sequence
。这也就是为什么,取出第一个元素的属性只存在于Collection中,而不是在Sequence
中,因为改这个first
方法一定要是nondestructive
的,也就是说取出第一个元素不应该对其输出结果产生影响,也就是说必须是稳定的。
怎样判断一个Sequence
是否是稳定的?
如果一个Sequence
遵守Collection
,那么它就是稳定的。
反过来,就不成立了:如果一个Sequence
是稳定的,那么它就是遵守Collection
协议的。这时不成立的。比如:StrideTo
和StrideThrough
类型,就是稳定的,然而他们没有遵守Collection
协议。
Sequences和Iterator之间的关系是怎样的?
Sequences和Iterator那么相似,为什么不把他们合并成一个呢?在destructively consumed sequence
中可以,因为他们可以共用一个Iterator,但是在stable sequence
中是不行的,因为它们需要Iterator提供的隔离的遍历状态和遍历逻辑(这种遍历状态就是Iterator创建的)。makeIterator
也就是为了创建这种遍历状态。
SubSequence
Sequence有另外的一个关联属性SubSequence
1 | public protocol Sequence { |
我们可以对其SubSequence
做以限制,比如:
1 | extension Sequence where Iterator.Element: Equatable, |
Collection Protocol
如上文所示Collection都是稳定的Sequence
,它可以稳定的被屡次遍历,并且没有破坏。并且其元素可以使用脚标的形式被访问。Colllection的Index
通常是integer
,存在数组中,并且是有限的。也就是说不像Sequence
,Collection
必须是有限的。这也就是为啥其有count
属性。
不仅仅标准库中的Array
,Set
,Dictionary
,CountableRange
以及UnsafeBufferProinter
等等遵守Collection
协议,Foundation库中的Data
和IndexSet
也遵守Collection
协议。
A Queue Implementation中对算法的解释不是很懂。
如果一个Type要遵守Collection
协议,那么它需要满足以下要求:
- 提供startIndex属性
- 提供endIndex属性
- 提供一个subscript,它至少是readonly的,用来获取Type的Element
- index函数,来寻找Collection的Index
由于Collection的协议的很多方法都有默认实现,所以我们在没有特殊处理的时候不需要再实现其他的方法,比如我们定义完Collection之后就可以使用map
,flatMap
,filter
,sorted
,joined
等方法。
为了让我们定义的Collection使用字面量的形式来创建,我们需要实现ExpressibleByArrayLiteral
协议,该协议只有一个方法:
1 | public protocol ExpressibleByArrayLiteral { |
也就是在这个初始化方法里面调用自己的Designated
初始化方法。
注:
只要是遵守ExpressibleByArrayLiteral
的Type都可以使用如
1
let queue:FIFOQueue = [1,2,3,4]
这样的方式进行创建,至于创建的类型需要根据声明的类型以及上下文来推断,也就是说在利用[1,2,3,4]这样的方式进行初始化的时候,系统会自动的调用协议中规定的初始化方法。