徹底搞定集合之:Generators vs Sequences vs Collections

** 這是我的集合系列文章的第一篇,計劃從淺入深通過一系列文章將swift的集合相關內容徹底整理清楚,包括集合類相關的第三方代碼庫,最后自定義一個集合類型,把所有的內容用代碼貫穿起來。**

Generators, Sequences 和 Collections 是swift標準庫的重要組成部分,也是我們編程過程中最常用到的內容,所以理解它們是如何工作的可以很好的提升我們的基本功。如果你能打開playground運行一下本文的代碼,那么會更好的下面的內容。

Generators

我們可以把Generators看做是生成器,它的作用就是提供下一個元素,如果沒有下一個元素則返回nil,標識生產過程結束。從代碼層面看Generator封裝了迭代器的狀態以及迭代器接口。它通過提供一個叫做next()的方法來返回sequence中的下一個元素。

我們可以通過實現 GeneratorType 協議來獲得一個 Generator,

protocol GeneratorType {
 associatedtype Element
  mutating func next() -> Element?
}

要實現這個協議我們要做的事情并不多,我們只需要讓next()方法返回下一個元素,并且在沒有元素的時候返回nil就可以了。

import Foundation

func pow2(power: Int) -> Int {
    return Int(pow(2.0, Double(power)))
}

struct PowersOfTwoGenerator1 : GeneratorType {
    associatedtype Element = Int
    var power : Int = 0
    mutating func next() -> Element? {
        return pow2(power++)
    }
}

現在我們已經成功的獲得了一個 Generator,我們可以調用它了。

var n = 10
var g = PowersOfTwoGenerator1()
while n > 0 {
  n -= 1
  println(g.next()!)
}

我們只需要調用next()方法就可以源源不斷的獲得“下一個元素”了,雖然很簡單,但是有一些遺憾,我們只能通過外部的邏輯才能控制元素的個數(在while循環中控制 n
的個數),現在我們計劃把這個工作交給 Generator 自己。

struct PowersOfTwoGenerator2 : GeneratorType {
    associatedtype Element = Int
    var power : Int = 0
    let endPower : Int
    init(end : Int) {
        endPower = end
    }
    mutating func next() -> Element? {
        return (power < endPower) ? pow2(power++) : nil
    }
}

我們給 Generator 一個構造器,通過構造器來設置一個停止生產元素的條件。

var g2 = PowersOfTwoGenerator2(end:10)
while let x = g2.next() {
    println(x)
}

只做了一個小改動,是不是讓調用代碼簡潔了很多?

到這里我們可以看到,一個 Generator 能做的工作并不多,一但返回nil,Generator的工作就結束了。

Sequences

sequence首先是一個values的序列,它可以使用for in循環控制結構進行迭代。另一方面sequence是Generator的工廠類,它知道如何生產一個適合的Generator。

我們可以通過實現SequenceType協議來獲得一個sequence,這部分工作也不復雜。

struct PowersOfTwoSequence2 : SequenceType {
    associatedtype Generator = PowersOfTwoGenerator2
    let endPower : Int
    init(end: Int) {
        self.endPower = end
    }
    func generate() -> Generator {
        return Generator(end: self.endPower)
    }
}

太棒了,我們現在已經獲得一個sequence了,快用for in遍歷一下試試。

for x in PowersOfTwoSequence2(end:10) {
    println(x)
}

雖然 PowersOfTwoSequence2 已經可以工作了,但它的實現有一個遺憾,endPower這個屬性需要分別在 PowersOfTwoSequence2 與 PowersOfTwoGenerator2中初始化兩次,我們不能容忍一個屬性如此的放縱.

struct PowersOfTwoSequence4 : SequenceType {
    let endPower : Int
    init(end: Int) {
        self.endPower = end
    }
    func generate() -> AnyGenerator<Int> {
        var power : Int = 0
        let nextClosure : () -> Int? = {
            (power < self.endPower) ? pow2(power++) : nil
        }
        return AnyGenerator<Int>(nextClosure)
    }
}

在這段代碼里,我們讓 generate() 返回一個 AnyGenerator ,AnyGenerator實現了 GeneratorType 協議,并且可以通過閉包的方式來創建 next() 方法。我們創建一個閉包 nextClosure 并溝通構造器傳遞個 AnyGenerator ,從而完成了 Generator 的創建。因為閉包幫助我們綁定了 endPower ,所以我們解決了 之前屬性需要被初始化兩次的問題。

我們可以通過尾閉包的方式,進一步精簡代碼。

struct PowersOfTwoSequence5 : SequenceType {
    let endPower : Int
    init(end: Int) {
        self.endPower = end
    }
    func generate() -> AnyGenerator<Int> {
        var power : Int = 0
        return AnyGenerator<Int> {
            (power < self.endPower) ? pow2(power++) : nil
        }
    }
}

Collections

一個 collection就是一個實現了 startIndex 和 endIndex 并且可以通過下標(subscript)訪問的sequence。collection比sequence更進一步的做到了,允許單個元素可以重復訪問。

集合協議擴展了SequenceType。

public protocol CollectionType : Indexable, SequenceType {
    public var startIndex: Self.Index { get }
    public var endIndex: Self.Index { get }
    public subscript (position: Self.Index) -> Self._Element { get }
}

實現一個集合協議的工作比實現一個SequenceType的工作稍微多一些。

    
struct PowersOfTwoCollection : CollectionType {
    associatedtype Index = Int
    let startIndex : Int
    let endIndex : Int
    init(start:Int, end: Int) {
        self.startIndex = start
        self.endIndex = end
    }
    func generate() -> AnyGenerator<Int> {
        var power : Int = 0
        return AnyGenerator<Int> {
            (power < self.endIndex) ? pow2(power++) : nil
        }
    }
    subscript(i: Index) -> Int { return pow2(i) }
}

我們在 PowersOfTwoSequence5 的基礎很容實現 CollectionType 協議。通過構造函數,為collection初始化 startIndex 與 endIndex。提供subscript來為collection提供下標訪問的能力。現在我們擁有一個集合了。

for x in reverse(PowersOfTwoCollection(start:0,end:10)) {
    println(x)
}

關于集合,這里只是剛剛開始,如果大家希望了解更多的集合內容,歡迎大家關注我后面的更新。我正在深入的研究swift語言,并將我的學習內容不斷的總結出來,希望能和大家交流共同進步。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 從三月份找實習到現在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發崗...
    時芥藍閱讀 42,372評論 11 349
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,466評論 25 708
  • 我的表妹小喬,人如其名,略有幾分姿色,喜古風、愛繪畫,自視清高,凡夫俗子皆不入眼,年近三十仍形單影只,近日發表一通...
    亦花閱讀 816評論 5 3
  • 今天是端午節,天好陰,斷續下著雨,是在紀念屈原嗎?我一個人在屋里,從昨晚開始到現在,沒有飯吃,沒人說話,就這么過了
    擺好pose很重要閱讀 290評論 0 0
  • 晚上,B妹子和同學打電話,提到了這個年紀最該談論的話題-戀愛與婚姻。 “沒時間啊,而且我還是一個顏控,長得好看的又...
    伍藝邇閱讀 291評論 0 0