Swift 是一門(mén)可以讓你按照自己喜歡的方式寫(xiě)代碼的語(yǔ)言,它有很強(qiáng)的擴(kuò)展能力批销,而它眾多的 Collection Protocols 則是這種擴(kuò)展能力的提供者之一。
本文介紹一下 Sequence 和 Iterator 的基本概念,及如何實(shí)現(xiàn)一個(gè) Sequence堂鲜。
撰寫(xiě)本文時(shí)的 Swift 版本是 Swift 3.1。
Sequence
Sequence 是一系列相同類型的值的集合护奈,并且提供對(duì)這些值的迭代能力缔莲。
迭代一個(gè) Sequence 最常見(jiàn)的方式就是 for-in
循環(huán),如下:
for element in someSequence {
doSomething(with: element)
}
我們經(jīng)常把 for-in
循環(huán)用在 Array霉旗、Dictioanry痴奏、Set 等數(shù)據(jù)結(jié)構(gòu)上蛀骇,因?yàn)樗麄兌紝?shí)現(xiàn)了 Sequence 協(xié)議。
Sequence 協(xié)議的定義:
protocol Sequence {
associatedtype Iterator: IteratorProtocol
func makeIterator() -> Iterator
}
Sequence 協(xié)議只有一個(gè)必須實(shí)現(xiàn)的方法 makeIterator()
makeIterator()
需要返回一個(gè) Iterator读拆,它是一個(gè) IteratorProtocol 類型擅憔。
也就是說(shuō)只要提供一個(gè) Iterator 就能實(shí)現(xiàn)一個(gè) Sequence,那么 Iterator 又是什么呢建椰?
Iterator
Iterator 在 Swift 3.1 標(biāo)準(zhǔn)庫(kù)中即為 IteratorProtocol雕欺,它用來(lái)為 Sequence 提供迭代能力。對(duì)于 Sequence棉姐,我們可以用 for-in
來(lái)迭代其中的元素屠列,其實(shí) for-in
的背后是 IteratorProtocol 在起作用。
IteratorProtocol 的定義如下:
public protocol IteratorProtocol {
associatedtype Element
public mutating func next() -> Self.Element?
}
其中僅聲明了一個(gè) next()
方法伞矩,用來(lái)返回 Sequence 中的下一個(gè)元素笛洛,或者當(dāng)沒(méi)有下一個(gè)元素時(shí)返回 nil
。
associatedtype 聲明了元素的類型乃坤。
舉個(gè)例子苛让,創(chuàng)建一個(gè)動(dòng)物的數(shù)組,對(duì)齊進(jìn)行循環(huán)遍歷:
let animals = ["Antelope", "Butterfly", "Camel", "Dolphin"]
for animal in animals {
print(animal)
}
// 打印結(jié)果:
Antelope
Butterfly
Camel
Dolphin
實(shí)際上編譯器會(huì)把以上代碼轉(zhuǎn)換成下面的代碼:
var animalIterator = animals.makeIterator()
while let animal = animalIterator.next() {
print(animal)
}
- 獲取到 animals 數(shù)組的 Iterator
- 在一個(gè) while 循環(huán)中湿诊,通過(guò) Iterator 不斷獲取下一個(gè)元素狱杰,并對(duì)元素進(jìn)行操作
- 當(dāng)
next()
返回nil
時(shí),退出循環(huán)
Iterator 實(shí)現(xiàn)舉例
舉例1:最簡(jiǎn)單的 Iterator
最簡(jiǎn)單的 Iterator 實(shí)現(xiàn)就是在 next()
中返回 nil
厅须,代碼如下:
struct SimplestIterator: IteratorProtocol {
typealias Element = Int
mutating func next() -> Int? {
return nil
}
}
這時(shí)這個(gè) Iterator 不會(huì)迭代出任何元素仿畸,確切的說(shuō),這個(gè) Interator 在迭代時(shí)僅調(diào)用一次 next()
就結(jié)束了朗和。
舉例2:常量 Iterator
讓 next()
返回一個(gè)值错沽,也是一種簡(jiǎn)單的實(shí)現(xiàn),例如下面代碼:
struct ConstantIterator: IteratorProtocol {
typealias Element = Int
mutating func next() -> Int? {
return 1
}
}
上面這個(gè)例子實(shí)現(xiàn)了一個(gè)只返回 1 的 Iterator眶拉。
舉例3:斐波那契數(shù)列 Iterator
再看一個(gè)復(fù)雜一點(diǎn)的例子千埃,斐波那契數(shù)列的實(shí)現(xiàn):
struct FibsIterator: IteratorProtocol {
var state = (0, 1)
mutating func next() -> Int? {
let upcomingNumber = state.0
state = (state.1, state.0 + state.1)
return upcomingNumber
}
}
- state 具有一個(gè)初始值 (0, 1),用元組表示
- 每次調(diào)用
next()
時(shí)忆植,?返回 state 元組中的第一個(gè)元素放可,然后更新 state 為 (第二個(gè)元素的值,第一個(gè)元素 + 第二個(gè)元素的和)
在實(shí)現(xiàn) Iterator 時(shí)可以省略 Element 的類型聲明朝刊,Swift 會(huì)通過(guò) next() 的返回值類型來(lái)自動(dòng)推導(dǎo)出 Element 的類型吴侦。不過(guò)對(duì)于實(shí)現(xiàn)比較復(fù)雜的 Iterator,往往還是會(huì)加上類型聲明這一句坞古,提高代碼可讀性。
實(shí)現(xiàn)一個(gè) Sequence
實(shí)現(xiàn)一個(gè) Sequence 首先要實(shí)現(xiàn)一個(gè) Iterator劫樟。
我們準(zhǔn)備實(shí)現(xiàn)這樣的一個(gè) Iterator:它接收一個(gè)字符串?dāng)?shù)組痪枫,并可以迭代這個(gè)數(shù)組中所有字符串的首字母织堂。
當(dāng)數(shù)組中的最后一個(gè)字符串迭代完畢后,退出迭代奶陈。
代碼如下:
struct FirstLetterIterator: IteratorProtocol {
let strings: [String]
var offset: Int
init(strings: [String]) {
self.strings = strings
offset = 0
}
mutating func next() -> String? {
guard offset < strings.endIndex else { return nil }
let string = strings[offset]
offset += 1
return string.substring(to: string.index(string.startIndex, offsetBy: 1))
}
}
- 這個(gè) Iterator 的需要輸入一個(gè)字符串?dāng)?shù)組
- ?在
next()
中易阳,判斷邊界,并返回?數(shù)組中索引為 offset 的字符串的首字母吃粒,并把 offset 加 1
這里省去了 Element 類型的聲明潦俺,編譯器可以根據(jù) next() 的返回值類型推斷出 Element 的類型。
有了已經(jīng)實(shí)現(xiàn)好的 Iterator徐勃,就可以很簡(jiǎn)單的用它實(shí)現(xiàn) Sequence事示,在 makeIterator()
中返回這個(gè) Iterator 即可。
struct FirstLetterSequence: Sequence {
let strings: [String]
func makeIterator() -> FirstLetterIterator {
return FirstLetterIterator(strings: strings)
}
}
現(xiàn)在 Sequence 已經(jīng)實(shí)現(xiàn)好了僻肖,可以測(cè)試一下效果肖爵。
我們可以創(chuàng)建一個(gè) FirstLetterSequence,并用 for-in
循環(huán)對(duì)其迭代:
for letter in FirstLetterSequence(strings: ["apple", "banana", "orange"]) {
print(letter)
}
/* 打印結(jié)果:
a
b
o
*/
結(jié)果為打印出了數(shù)組中每個(gè)字符串的首字母臀脏。