从迭代语义中剥离出序列
有了上一节内容做铺垫,我们知道,必须得再通过一个类型来单独约束序列自身。于是,我们定义了下面这样的protocol
:
protocol Sequence {
associatedtype Element
associatedtype Iterator: IteratorProtocol
where Iterator.Element == Element
}
分离序列的定义和遍历的动作
很简单,在Sequence
里,我们也用Element
表示序列中元素的类型,并且,用Iterator
表示从序列中读取下一个元素的对象的类型。显然,它要遵从IteratorProtocol
,并且,IteratorProtocol.Element
和Sequence.Element
必须是相同的。
接下来,我们还要约束一个方法,用于获取序列的Iterator
对象:
protocol Sequence {
/// ...
func makeIterator() -> Iterator
}
这样,之前的的Fibonacci
就可以定义成这样:
struct Fibonacci: Sequence {
typealias Element = Int
func makeIterator() -> FiboIter {
return FiboIter()
}
}
然后,我们定义与它配套使用的Iterator
:
struct FiboIter: IteratorProtocol {
var state = (0, 1)
mutating func next() -> Int? {
let nextNumber = state.0
self.state = (state.1, state.0 + state.1)
return nextNumber
}
}
这部分,和上一节是完全一样的。然后,我们就有了一个统一访问Sequence
类型的套路。首先,像这样来定义Sequence
:
var fibs = Fibonacci()
其次,当我们要逐个访问元素的时候,通过makeIterator
来获取访问方式:
var fib1 = fibs.makeIterator()
fib1.next() // Optional(0)
fib1.next() // Optional(1)
fib1.next() // Optional(1)
fib1.next() // Optional(2)
这样,上一节中反复遍历序列的问题也解决了。我们只要调用一次makeIterator
,就会生成一个可以重新遍历的Iterator
。
当然,实际上,我们完全不用自己定义Sequence
和IteratorProtocol
,Swift的标准库里,正是按照我们这两节里的思路,为我们准备好了现成的定义。我们可以把自己定义的这两个protocol
注释掉。然后试着直接用for
遍历一下fibs
:
for n in Fibonacci() {
if (n <= 5) {
print(n)
}
else {
break
}
}
执行一下就会看到,它们可以搭配在一起完美工作。
引用类型的Iterator
在我们的例子中,FiboIter
是个struct
。也就是说,它是值语义的类型。当我们拷贝了FiboIter
之后,每一个对象之后就都会按照它们自己的“进度”遍历序列。这是个很符合我们预期的操作:
var fib2 = fib1
fib1.next() // Optional(3)
fib1.next() // Optional(5)
fib2.next() // Optional(3)
fib2.next() // Optional(3)
在上面的例子中可以看到,给fib2
赋值的时候,它拷贝了当时的迭代状态。在接下来的调用里,fib1
和fib2
就按照自己的进度进行遍历了。
但是,如果我们希望Iterator
有引用类型的语义该怎么办呢?我们要从两个方面来回答这个问题:
一来,如果我们认为Iterator
的值语义是符合直觉的,那么引用语义的Iterator
背后的含义则是:这个序列的所有Iterator
都是一样的。这个假设,是针对类型的使用者而言的;
二来,对于类型的设计者而言,如果要创建一个引用语义的Iterator
,想必一定是有他自己独特的原因。为此,最好让他就彻底把这个“有违直觉”的类型隐藏起来;
因此,Swift设计了一个叫做AnyIterator
的类型,它是一个Iterator
的包装,经过包装之后的Iterator
就变成引用语义的了:
var fib3 = AnyIterator(fib2)
var fib4 = fib3
fib3.next() // Optional(8)
fib3.next() // Optional(13)
fib4.next() // Optional(21)
fib4.next() // Optional(34)
现在,结合刚才我们提到的两点考量,你就更能领会所谓Any
的含义了。
其实没有那么黑白分明的Iterator和Sequence
当然,无论是值类型还是引用类型的Iterator
,终究,我们用两个独立的类型,分别表示了序列本身和逐个访问序列这个动作。不过,至此,你应该明白了,这样的区分完全是为了语义上更清楚,序列是序列,动作是动作;序列中保存的,是序列的值,动作中保存的,是迭代的状态。但你也不能否认,一个遵从IteratorProtocol
的类型,完全有可能就是一个Sequence
。本来嘛,你无论如何也不能否认,我们在上一节定义定义ones
就是一个序列啊。甚至,Swift标准库的绝大多数Iterator
类型,也都遵从了Sequence
。因此,理解这两个protocol
存在的理由至关重要,我们得辩证的看待这个问题。