Sequence
是整个Swift标准库中整个集合类型体系的起点,它抽象了一个集合最朴素的形式。有多朴素呢?用文字描述出来就是:仅表示一系列类型相同的元素,而不对这一系列元素的性质有任何额外的约定。例如:是否可以多次遍历,是否可以逆序排列,甚至是否包含有限个元素等都没有约定。它唯一约定了的动作,就是从序列当前位置读取下一个元素。
从定义理解Sequence
了解了Sequence
的想法之后,我们直接来看Swift中对应的实现。这部分代码在stdlib/public/core/Sequence.swift里:
public protocol Sequence {
associatedtype Element
func makeIterator() -> Iterator
}
实际上,在Sequence.swift开始,有一大段官方的注释解释了这个protocol
设计的想法,看过这个系列的内容之后,推荐大家去读一下它们。不过现在,还是顺着我们的思路继续。可以看到,Sequence
约定的内容非常简单:
-
Element
表示序列中元素的类型; -
makeIterator
则返回一个Iterator
对象,所谓的Iterator
,可以把它看成一个了解序列内部构造的黑盒子,可以帮助我们用一致的接口获取任何序列的“下一个”元素。它的定义在这里:
public protocol IteratorProtocol {
associatedtype Element
mutating func next() -> Element?
}
其中:
-
Element
表示这个接口返回的元素类型; -
next()
方法,就是刚才说到的访问序列下一个元素的接口。由于不一定可以从序列中获取到下一个元素,因此,next
的返回值是Element?
。至于为什么next
要约束成一个mutating
方法,我们稍后用两个例子来解释。
看到这你可能会想了,Sequence.makeIterator
返回的对象中Element
的类型,和IteratorProtocol
中的Element
应该是一样的啊。事实上,的确如此,在Sequence的实现里,Iterator
的定义是这样的:
public protocol Sequence {
associatedtype Iterator : IteratorProtocol
where Iterator.Element == Element
}
最后,把Sequence
完整的定义列出来:
public protocol IteratorProtocol {
associatedtype Element
mutating func next() -> Element?
}
public protocol Sequence {
associatedtype Iterator : IteratorProtocol
where Iterator.Element == Element
func makeIterator() -> Iterator
}
这就是Swift中,最朴实的一类集合的约定。对于一个遵从了Sequence
的类型,就像我们在一开始说的一样,我们不知道它包含的元素个数、不知道它是否可以反复遍历、不知道它是否可以逆序。我们能做的,就是通过它的Iterator
获取集合中的下一个元素,而这也是我们唯一可以做的事情。
尽管Swift还通过extension
给Sequence
添加了很多方法,但它们并不是Sequence
约束的一部分。也就是说,当自定义一个遵从了Sequence
的类型时,不用重新实现它们。
三种不同性质的Sequence
接下来,为了更具象的了解Sequence
类型的“质朴”,我们来看三个具体的例子。第一个例子,是我们最容易理解的数组。把它“降维”理解成一个序列完全没问题:
let array = [1, 2, 3, 4, 5, 6]
对于array
表示的这个数字序列来说,如果不把它当数组看,我们访问它的元素就只能依靠不断调用Array.Iterator
的next
方法:
var ia = arr.makeIterator()
while let item = ia.next() {
print(item)
}
显然,这种从数组“降维”而来的序列有有限个元素并且支持重复遍历。
第二个例子,Sequence
表示的序列也可以是包含无限个元素的(一直到序列中元素值溢出),例如一个Fibonacci数列。为了表示这样的类型,我们先定义对应的Iterator
:
struct FibonacciIterator: IteratorProtocol {
// typealias int = Element
var state = (curr: 0, next: 1)
mutating func next() -> Int? {
let curr = state.curr
state = (curr: state.next, next: state.curr + state.next)
return curr
}
}
然后再来定义数列自身:
struct Fibonacci: Sequence {
// typealias int = Element
// typealias Iterator = FibonacciIterator
func makeIterator() -> FibonacciIterator {
return FibonacciIterator()
}
}
在注释里,我分别列出了FibonacciIterator
和Fibonacci
中对应的associatedtype
类型,为的是帮助我们理解它的定义。实际上这是不必要的,Swift可以根据makeIterator
以及next
的返回值推导出它们。
现在,思考一个问题,该如何表达一个Fibonacci数列呢?我给你下面两个答案,你选哪个:
/// A
var fibA = FibonacciIterator()
/// B
var fibB = Fibonacci()
是A还是B呢?其实答案是无论哪个都可以。对于fibA
来说,我们的侧重点是事实,毕竟只要像下面这样就可以得到数列了:
fibA.next()
fibA.next()
fibA.next()
/// ...
而对于fibB
来说,我们的侧重点是形式,Fibonacci数列是一个Sequence
。按照约定,为了访问Sequence
的每一个成员,我们要获取它的专属Iterator
,然后通过next
方法完成:
var iterB = fibB.makeIterator()
iterB.next()
iterB.next()
iterB.next()
/// ...
说到这,我们再来回答一个问题,既然FibonacciIterator
自身就可以表达一个序列,为什么还要单独定义Fibonacci
呢?或者说,为什么要分开Iterator
和Sequence
呢?
这个问题的答案和两个因素相关,它们是:Sequence
的值和Iterator
的状态。通常,Iterator
为了可以持续遍历序列,它在内部都要保存一个状态。当这个状态和Sequence
要表达的值一致的时候,Iterator
和Sequence
的界限就会变得模糊,甚至可以说Iterator
自身就表示了这个序列。为了看到这个效果,我们可以直接让FibonacciIterator
遵从Sequence
:
struct FibonacciIterator: IteratorProtocol, Sequence {
/// The same as before
/// ...
}
这样,FibonacciIterator
就“名正言顺”的成为了一个序列,我们也不用为它专门编写makeIterator
方法。这是因为,在Sequence
代码里,Swift官方已经为这种情况提供了一个默认实现:
/// A default makeIterator() function for `IteratorProtocol` instances that
/// are declared to conform to `Sequence`
extension Sequence where Self.Iterator == Self {
/// Returns an iterator over the elements of this sequence.
@inlinable
public __consuming func makeIterator() -> Self {
return self
}
}
但绝大多数时候,Iterator
保持的状态,和Sequence
的值,并不是一致的。这时,就必须通过独立的类型来表达Iterator
和Sequence
了,而这就是我们要演示的第三类Sequence
。这次,我们定义一个由用户输入的每一行内容形成的序列。
先来定义Iterator
:
struct InputStreamIterator: IteratorProtocol {
var currLine = 0
mutating func next() -> String? {
guard let input = readLine() else { return nil }
currLine += 1
print("(\(currLine)) \(input)")
return input
}
}
这次,Iterator
中用于维系遍历的状态,是currLine
,表示这是当前第几行输入,然后在控制台打印类似(1) Hello world
这种形式的字符串,并返回当前用户输入的内容。
接下来,定义表示这个序列的类型:
struct InputStream: Sequence {
func makeIterator() -> InputStreamIterator {
return InputStreamIterator()
}
}
最后,用下面的代码来测试:
let stdin = InputStream()
var stdinIter = stdin.makeIterator()
while true {
guard let _ = stdinIter.next() else { break }
}
把这些代码保存在demo.swift里,并执行swiftc demo.swift -o demo
。编译完成后,执行demo
,随着在控制台中一行行输入内容,就可以看到类似下面这样的结果了:
ha
(1) ha
haha
(2) haha
hahaha
(3) hahaha
hahahaha
(4) hahahaha
通过这三个例子,我们应该可以彻底明白Sequence
要表达的概念了。对于一系列类型相同的序列来说:
- 它的值可以是预分配的,例如数组中的元素。也可以是
lazy
的,例如Fibonacci
中的访问时现算,或者InputStream
中用户的当前输入; - 它的元素个数可以是有限的(
array
),也可以是无限的(Fibonacci / InputStream
); - 它可以被反复遍历(
array / Fibonacci
),或者只能遍历一次(InputStream
);