# Swift 集合型別之迭代器

語歌發表於2017-10-15

Blai
Blai

我們知道在 SwiftSequence集合型別結構 中的基礎。而在 Sequence 協議的定義當中我們可以看到有這麼一句程式碼 :

associatedtype Iterator : IteratorProtocol複製程式碼

我們看到有兩個關鍵字:associatedtypeIteratorProtocol

1.associatedtype

那麼這個 associatedtype 是什麼意思呢?
看個例子就知道了:

第一步:

我們定義兩個協議 A,B,協議 B 中用到了協議 A

protocol A {}
protocol B {
    func action(_ pA2: A)
}複製程式碼
第二步:

那麼我們對協議 A 有兩個實現: A1A2.
我們對協議 BB1 的實現.程式碼如下:

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

struct B1: B {
    func action(_ pA2: A) {
        print("我只認對協議A實現的A2")
    }
}複製程式碼

這個時候如果我們要使用 B1 的時候,可以這樣:

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

///我只認對協議A實現的A2
///我只認對協議A實現的A2複製程式碼
第三步: 條件

這個時候我們在 B1 中對傳入協議 A 的型別想要指定的型別實現而傳入非指定型別的實現編譯器就會報錯怎麼做?
有人講這樣:

struct B1: B {
    func action(_ pA2: A2) {
        print("我只認對協議A實現的A2")
    }
}複製程式碼

這樣子編譯器就會報錯:Type 'B1' does not conform to protocol 'B'
這個時候我們的 associatedtype 就可以登場了:

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("我只認對協議A實現的A2")
    }
}

let action = B1()
action.action(A2())
///action.action(A1())  這個實現就直接報錯了:error: OptimizingCollections.playground:16:15: error: cannot convert value of type 'A1' to expected argument type 'A2' action.action(A1())複製程式碼

associatedtype 總結就仁者見仁各有各的理解啦!小插曲結束進入正題:↓

2.IteratorProtocol 與迭代器的邂逅

按住 Command 點進去會發現該協議只有一個函式:

associatedtype Element
public mutating func next() -> Self.Element?複製程式碼

以下用 next() 來表示.

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

迭代器 是一個滿足 IteratorProtocol 協議的型別。

普及一個知識:

序列 (Sequence)

具有相同型別的值的一個集合吧。比如簡單的: [Int] ... 都是滿足 Sequence 協議
的。


那麼有了序列就會有需要對值遍歷的訪問,那麼就是靠建立一個 迭代器 來進行對元素的訪問。
那麼 next() 的作用就是每次呼叫的時候返回序列的下一個值,直到最後一個返回 nil

這裡來開始剖析:

Self.Element

它是一個關聯型別用來指定 next() 所產生值的元素型別,比如我們經常見到的 Iterator.Element 就是 IteratorProtocol 中的定義 Element。那麼迭代器一般會用在那裡呢?

答:自定義序列的擴充套件型別,比如你想通過字串 "YinYu" 轉換為:"["Y", "i", "n", "Y", "u"]" 的擴充套件類等等等等多的用途!

迭代器在結構上是單向的,顧名思義只能一路 next
對於迭代器類似的操作我們一般都是用

for <#item#> in <#items#> {
    <#code#>
}複製程式碼

來完成。 這個一般情況是不推薦使用的。我們要玩就要玩高階的。不然怎麼成長呢?是吧!

接下來我們來完成這樣一個例子的實現,來為您以後 “拋磚引玉” 一下

首先完成一個 PrefixIterator 實現 IteratorProtocol協議的 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]))
那麼有了迭代器,就需要一個裝載迭代器的且滿足 Sequence 協議的 struct.
因為在 Sequence 中我們發現有這樣的一個函式:

associatedtype Iterator : IteratorProtocol

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

那麼這裡我們的 PrefixIterator 就相當於 Self.Iterator 型別.
接下來就實現對 Sequence協議的構造。

public struct PrefixSequence: Sequence {
    let string: String
    public func makeIterator() -> PrefixIterator {
        return PrefixIterator(string: string)
    }
}複製程式碼

既然實現了 Sequence協議,那麼我們就可以得到 Map, filter, forEach, dropFirst... 這些集合型別常用的函式方法。
利用 Map依次返回一個字母。從而達到 "YinYu""Y", "i", "n", "Y", "u" 的轉換
這樣一個 String 擴充套件方法就出來了:

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

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

值語義

我們都知道 結構體(Struct) 列舉(enum)值型別

當建立一個 class(類) 的時候,class 是 引用型別

對於引用型別的操作我們是要萬分小心的,引用型別是具有統一性的一般用 === 判斷兩個變數是否引用了同一個 object。即:指標相等
== 呢? 當然是: 結構相等

一般我們通過值型別是否執行 深複製來判斷是否具有 值語義.

有深複製就會有 潛複製:
Struct 中包含引用型別,這個時候 Struct 進行賦值給其它的變數的時候所發生的複製行為會存在引用型別的內容是不會複製,其引用本身會被複制。這種行為就是 淺複製.


AnyIterator

可以對其它的迭代器進行一個包裝,從而迷惑使用者。

這裡要說的就是 AnyIterator 在這樣的情況下 var 新迭代器 = AnyIterator(舊迭代器) 的時候, 新迭代器是 不具有 值語義的。這種情況下舊迭代器與新迭代器就不是單獨的了,新迭代器就不是一個結構體。 新迭代器是以一個類例項。
下面程式碼見分曉:

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 也是對 IteratorProtocol 協議的實現.那麼配合 nest() 與對應的 AnySequence,可以得到在不定義任何新型別的情況下建立迭代器序列。

語歌部落格: Blog.aiyinyu.com
語歌-Blair

相關文章