前言

生成器(Generators)与序列(Sequences)构成了Swift式循环。

生成器(Generators)

提到数组我们就会想到遍历,一般的遍历可能都是从头到尾进行的。但是如果你有特殊的需求呢。你可能不想呆板的进行遍历。这时候Generators就可以派上用场了。
Generators的存在是进行特殊癖好的数组遍历,其筛选出符合该癖好的下标索引到数组没有元素为止。
任意一个generator都需要遵从如下协议:

1
2
3
4
protocol GeneratorType { 
associatedtype Element
func next() -> Element?
}

根据上述协议,该协议需要元素类型以及一个next()函数。

举个倒序索引的generator:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class CountdownGenerator: GeneratorType {
typealias Element = Int
var element: Element

init<T>(array: [T]) {
self.element = array.count - 1
}

func next() -> Element? {
return self.element < 0 ? nil : element--
}
}

let xs = ["A", "B", "C"]
let generator = CountdownGenerator(array: xs)
while let i = generator.next() {
println("Element \(i) of the array is \(xs[i])")
}

输出:

Element 2 of the array is C

Element 1 of the array is B

Element 0 of the array is A

优点:

尽管这个小例子看起来有点小题大做,可生成器却封装了数组序列值的计算。如果你想要用另一种方式排序序列值,我们只需要更新生成器,而不必再修改这里的代码。

序列(Sequences)

Generators在循环过程中每个元素提供的服务是一次性的。所以我们想做倒回操作的话需要生成一个新的generator。若不想这样则需要用上sequence,其遵从另外一个协议SequenceType:

1
2
3
4
protocol SequenceType {
associatedtype Generator: GeneratorType
func generate() -> Generator
}

通过协议我们知道每个sequence都与一个generator类型已经一个generator构造器绑定在一起。我们可以使用这个遍历sequence。
举个栗子,我们可以使用CountdownGenerator来定义一个sequence从而生成一个倒序的数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct ReverseSequence<T>: SequenceType {
var array: [T]
init(array: [T]) {
self.array = array
}

typealias Generator = CountdownGenerator

func generate() -> Generator {
return CountdownGenerator(array: array)
}
}

let xs = ["A", "B", "C"]
let reverseSequence = ReverseSequence(array: xs)
let reverseGenerator = reverseSequence.generate()
while let i = reverseGenerator.next() {
println("Index \(i) is \(xs[i])")
}

输出:

Index 2 is C

Index 1 is B

Index 0 is A

Swift在处理序列时有一个特别的语法。不同于创建一个序列的关联生成器,你可以编写一个for-in循环。比如,我们也可以将上面的代码段写成这样:

1
2
for i in ReverseSequence(array: xs) {
print("Index \(i) is \(xs[i ])") }

输出:

Index 2 is C

Index 1 is B

Index 0 is A

实际上,Swift做的只是使用generate方法生成了一个生成器,然后重复地调用其next函数直到返回nil。

优点:

对比之前仅仅使用生成器的例子,同一个序列可以被第二次遍历–为此我们只需要调用generate来生成一个新的生成器就可以了。