# Swift 集合類型之迭代器

Blai

我們知道在 SwiftSequence集合類型結(jié)構(gòu) 中的基礎(chǔ)。而在 Sequence 協(xié)議的定義當(dāng)中我們可以看到有這么一句代碼 :

associatedtype Iterator : IteratorProtocol

我們看到有兩個(gè)關(guān)鍵字:associatedtypeIteratorProtocol

1.associatedtype

那么這個(gè) associatedtype 是什么意思呢?
看個(gè)例子就知道了:

第一步:

我們定義兩個(gè)協(xié)議 A,B,協(xié)議 B 中用到了協(xié)議 A

protocol A {}
protocol B {
    func action(_ pA2: A)
}
第二步:

那么我們對(duì)協(xié)議 A 有兩個(gè)實(shí)現(xiàn): A1A2.
我們對(duì)協(xié)議 BB1 的實(shí)現(xiàn).代碼如下:

struct A1: A {}
struct A2: A {}

struct B1: B {
    func action(_ pA2: A) {
        print("我只認(rèn)對(duì)協(xié)議A實(shí)現(xiàn)的A2")
    }
}

這個(gè)時(shí)候如果我們要使用 B1 的時(shí)候,可以這樣:

let action = B1()
action.action(A2())
action.action(A1())

///我只認(rèn)對(duì)協(xié)議A實(shí)現(xiàn)的A2
///我只認(rèn)對(duì)協(xié)議A實(shí)現(xiàn)的A2

第三步: 條件

這個(gè)時(shí)候我們?cè)?B1 中對(duì)傳入?yún)f(xié)議 A 的類型想要指定的類型實(shí)現(xiàn)而傳入非指定類型的實(shí)現(xiàn)編譯器就會(huì)報(bào)錯(cuò)怎么做?
有人講這樣:

struct B1: B {
    func action(_ pA2: A2) {
        print("我只認(rèn)對(duì)協(xié)議A實(shí)現(xiàn)的A2")
    }
}

這樣子編譯器就會(huì)報(bào)錯(cuò):Type 'B1' does not conform to protocol 'B'
這個(gè)時(shí)候我們的 associatedtype 就可以登場(chǎng)了:

protocol A {}
protocol B {
    associatedtype F: A
    func action(_ pA2: F)
}

struct A1: A {}
struct A2: A {}

struct B1: B {
    func action(_ pA2: A2) {
        print("我只認(rèn)對(duì)協(xié)議A實(shí)現(xiàn)的A2")
    }
}

let action = B1()
action.action(A2())
///action.action(A1())  這個(gè)實(shí)現(xiàn)就直接報(bào)錯(cuò)了:error: OptimizingCollections.playground:16:15: error: cannot convert value of type 'A1' to expected argument type 'A2' action.action(A1())

對(duì) associatedtype 總結(jié)就仁者見仁各有各的理解啦!小插曲結(jié)束進(jìn)入正題:↓

2.IteratorProtocol 與迭代器的邂逅

按住 Command 點(diǎn)進(jìn)去會(huì)發(fā)現(xiàn)該協(xié)議只有一個(gè)函數(shù):

associatedtype Element
public mutating func next() -> Self.Element?

以下用 next() 來(lái)表示.

可別看它就這么一句;,但是 能量 很大?。?!

迭代器 是一個(gè)滿足 IteratorProtocol 協(xié)議的類型。

普及一個(gè)知識(shí):

序列 (Sequence)

具有相同類型的值的一個(gè)集合吧。比如簡(jiǎn)單的: [Int] ... 都是滿足 Sequence 協(xié)議
的。


那么有了序列就會(huì)有需要對(duì)值遍歷的訪問,那么就是靠創(chuàng)建一個(gè) 迭代器 來(lái)進(jìn)行對(duì)元素的訪問。
那么 next() 的作用就是每次調(diào)用的時(shí)候返回序列的下一個(gè)值,直到最后一個(gè)返回 nil

這里來(lái)開始剖析:

Self.Element

它是一個(gè)關(guān)聯(lián)類型用來(lái)指定 next() 所產(chǎn)生值的元素類型,比如我們經(jīng)常見到的 Iterator.Element 就是 IteratorProtocol 中的定義 Element。那么迭代器一般會(huì)用在那里呢?

答:自定義序列的擴(kuò)展類型,比如你想通過字符串 "YinYu" 轉(zhuǎn)換為:"["Y", "i", "n", "Y", "u"]" 的擴(kuò)展類等等等等多的用途!

迭代器在結(jié)構(gòu)上是單向的,顧名思義只能一路 next
對(duì)于迭代器類似的操作我們一般都是用

for <#item#> in <#items#> {
    <#code#>
}

來(lái)完成。 這個(gè)一般情況是不推薦使用的。我們要玩就要玩高級(jí)的。不然怎么成長(zhǎng)呢?是吧!

接下來(lái)我們來(lái)完成這樣一個(gè)例子的實(shí)現(xiàn),來(lái)為您以后 “拋磚引玉” 一下

首先完成一個(gè) PrefixIterator 實(shí)現(xiàn) IteratorProtocol協(xié)議的 struct。

public struct PrefixIterator: IteratorProtocol {
    let string: String
    var offset: String.Index
    init(string: String) {
        self.string = string
        offset = string.startIndex
    }
    mutating public func next() -> String? {
        guard offset < string.endIndex else { return nil }
        let previousSet = offset
        offset = string.index(after: offset)
        return String(string[previousSet..<offset])
    }
}

我們分析上面主要有: mutating,字符串切片(String(string[previousSet..<offset]))
那么有了迭代器,就需要一個(gè)裝載迭代器的且滿足 Sequence 協(xié)議的 struct.
因?yàn)樵?Sequence 中我們發(fā)現(xiàn)有這樣的一個(gè)函數(shù):

associatedtype Iterator : IteratorProtocol

///Returns an iterator over the elements of this sequence
public func makeIterator() -> Self.Iterator

那么這里我們的 PrefixIterator 就相當(dāng)于 Self.Iterator 類型.
接下來(lái)就實(shí)現(xiàn)對(duì) Sequence協(xié)議的構(gòu)造。

public struct PrefixSequence: Sequence {
    let string: String
    public func makeIterator() -> PrefixIterator {
        return PrefixIterator(string: string)
    }
}

既然實(shí)現(xiàn)了 Sequence協(xié)議,那么我們就可以得到 Map, filter, forEach, dropFirst... 這些集合類型常用的函數(shù)方法。
利用 Map依次返回一個(gè)字母。從而達(dá)到 "YinYu""Y", "i", "n", "Y", "u" 的轉(zhuǎn)換
這樣一個(gè) String 擴(kuò)展方法就出來(lái)了:

extension String {
    func stringToArr() -> [String] {
        return PrefixSequence(string: self).map { $0 }
    }
}
let strs = "YinYu".stringToArr()
/// ["Y", "i", "n", "Y", "u"]

當(dāng)然如果你想輸出的字面都是大寫很簡(jiǎn)單:
$0.uppercased() 這樣就OK啦.

值語(yǔ)義

我們都知道 結(jié)構(gòu)體(Struct) 枚舉(enum)值類型。

當(dāng)創(chuàng)建一個(gè) class(類) 的時(shí)候,class 是 引用類型。

對(duì)于引用類型的操作我們是要萬(wàn)分小心的,引用類型是具有統(tǒng)一性的一般用 === 判斷兩個(gè)變量是否引用了同一個(gè) object。即:指針相等
== 呢? 當(dāng)然是: 結(jié)構(gòu)相等

一般我們通過值類型是否執(zhí)行 深復(fù)制來(lái)判斷是否具有 值語(yǔ)義.

有深復(fù)制就會(huì)有 潛復(fù)制:
當(dāng) Struct 中包含引用類型,這個(gè)時(shí)候 Struct 進(jìn)行賦值給其它的變量的時(shí)候所發(fā)生的復(fù)制行為會(huì)存在引用類型的內(nèi)容是不會(huì)復(fù)制,其引用本身會(huì)被復(fù)制。這種行為就是 淺復(fù)制.


AnyIterator

可以對(duì)其它的迭代器進(jìn)行一個(gè)包裝,從而迷惑使用者。

這里要說(shuō)的就是 AnyIterator 在這樣的情況下 var 新迭代器 = AnyIterator(舊迭代器) 的時(shí)候, 新迭代器是 不具有 值語(yǔ)義的。這種情況下舊迭代器與新迭代器就不是單獨(dú)的了,新迭代器就不是一個(gè)結(jié)構(gòu)體。 新迭代器是以一個(gè)類實(shí)例。
下面代碼見分曉:

var ierator1 = stride(from: 0, to: 10, by: 1).makeIterator()

ierator1.next()   /// 0
ierator1.next()   /// 1

var ierator2 = ierator1

ierator1.next()   /// 2
ierator1.next()   /// 3

ierator2.next()   /// 2
ierator2.next()   /// 3

var ierator3 = AnyIterator(ierator1)
var ierator4 = ierator3

ierator3.next()   /// 4
ierator4.next()   /// 5

ierator3.next()   /// 6
ierator3.next()   /// 7

AnySequence

似乎與 IteratorSequence 一樣,也存在著 AnySequence.

AnyIterator 也是對(duì) IteratorProtocol 協(xié)議的實(shí)現(xiàn).那么配合 nest() 與對(duì)應(yīng)的 AnySequence,可以得到在不定義任何新類型的情況下創(chuàng)建迭代器序列。

語(yǔ)歌博客: Blog.aiyinyu.com
語(yǔ)歌-Blair

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容