集合類型(三)

集合類型模塊分四篇筆記來學習:

  • 第一篇:
  • 數(shù)組和可變性
  • 數(shù)組的變形
  • 第二篇:
    • 字典和集合
    • 集合協(xié)議
  • 第三篇:
    • 集合
  • 第四篇:
    • 索引

本篇開始學習第三篇,Go科吭!

集合

集合協(xié)議 CollectionType 是在序列上構(gòu)建的笋轨,它為序列添加了可重復進行迭代(由于序列是值類型代咸,所以不能單獨進行迭代)氧猬,以及通過索引訪問元素的能力。

為了展示 Swift 的集合類型是如何工作的脆烟,我們將實現(xiàn)一個我們自己的集合類型。隊列 (queue) 是在 Swift 標準庫中不存在房待,但卻又最常用到的容器類型了邢羔。Swift 的數(shù)組可以很容易地被用來實現(xiàn)棧 (stack) 結(jié)構(gòu),只需要使用 append 入棧和 popLast 出棧就可以了桑孩。但是將數(shù)組作為隊列來使用的話就不那么理想拜鹤。

為隊列設(shè)計協(xié)議

在實際實現(xiàn)隊列之前,我們應(yīng)該先定義它到底是什么流椒。我們可以通過下面定義的協(xié)議來描述隊列的特性:

/// 一個能夠?qū)⒃厝腙牶统鲫牭念愋?protocol QueueType {
  /// 在 `self` 中所持有的元素的類型
  associatedtype Element
  /// 將 `newElement` 入隊到 `self`
  mutating func enqueue(newElement: Element)
  /// 從 `self` 出隊一個元素
  mutating func dequeue() -> Element?”
}

隊列的實現(xiàn)

下面是一個很簡單的隊列署惯,它的 enqueue 和 dequeue 方法是基于一系列數(shù)組來構(gòu)建的。

/// 一個高效的 FIFO 隊列镣隶,其中元素類型為 `Element`
struct Queue<Element>: QueueType {
  private var left: [Element]
  private var right: [Element]

  init() {
    left = []
    right = []
  }

  /// 將元素添加到隊列最后极谊,復雜度 O(1)
  mutating func enqueue(element: Element) {
    right.append(element)
  }

  /// 從隊列前端移除一個元素,復雜度 O(1).
  /// 當隊列為空時安岂,返回 nil
  mutating func dequeue() -> Element? {
      guard !(left.isEmpty && right.isEmpty) else { return nil }
      if left.isEmpty {
        left = right.reverse()
        right.removeAll(keepCapacity: true)
      }
      return left.removeLast()
  }

至此我們實現(xiàn)了一個最簡單的隊列轻猖,接下來通過分別實現(xiàn)CollectionType、ArrayLiteralConvertible域那、RangeReplaceableCollectionType來擴展Queue咙边。

遵守 CollectionType

CollectionType 是對 Indexable 和 SequenceType 的擴展猜煮。該協(xié)議有兩個關(guān)聯(lián)類型和九個方法。不過败许,這兩個關(guān)聯(lián)類型都有默認值王带,而不少方法也有它們的默認實現(xiàn)。
我們要做的是為 Indexable 協(xié)議中要求的 startIndex 和 endIndex 提供實現(xiàn)市殷,并且實現(xiàn)一個通過下標索引來獲取對應(yīng)索引的元素的方法愕撰。只要我們實現(xiàn)了這三個需求,我們就能讓一個類型遵守 CollectionType 了醋寝。

我們現(xiàn)在已經(jīng)有一個能夠出隊和入隊的容器了搞挣,要將它轉(zhuǎn)變?yōu)橐粋€集合,Queue 需要遵守 CollectionType:

extension Queue: CollectionType {
    var startIndex: Int { return 0 }
    var endIndex: Int { return left.count + right.count }

    subscript(idx: Int) -> Element {
      precondition((0..<endIndex).contains(idx), "Index out of bounds")
      if idx < left.endIndex {
        return left[left.count - idx.successor()]
      } else {
        return right[idx - left.count]
      }
    }
}

有了這幾行代碼音羞,Queue 現(xiàn)在已經(jīng)遵守 CollectionType 了囱桨。因為 CollectionType 是基于 SequenceType 的,所以 Queue 也遵守 SequenceType⌒岽拢現(xiàn)在我們的隊列已經(jīng)擁有超過 40 個方法和屬性供我們使用了:

var q = Queue<String>()
for x in ["1", "2", "foo", "3"] {
  q.enqueue(x)
}

// 你可以用 for...in 循環(huán)訪問隊列
for s in q { print(s) } // 打印 1 2 foo 3

// 將其傳遞給接受序列的方法
q.joinWithSeparator(",") // "1,2,foo,3"
let a = Array(q) // a = ["1", "2", "foo", "3]

// 調(diào)用 SequenceType 的擴展方法
q.map { $0.uppercaseString } // ["1", "2", "FOO", "3"]
q.flatMap { Int($0) } // [1,2,3]


q.filter { // ["foo"]
    $0.characters.count > 1
}

// 調(diào)用 CollectionType 的擴展方法
q.isEmpty // false
q.count // 4
q.first // "1"
q.last // "3”

遵守 ArrayLiteralConvertible

實現(xiàn)ArrayLiteralConvertible 可以直接已字面量的方式賦值舍肠,實現(xiàn)這個協(xié)議非常容易:

extension Queue: ArrayLiteralConvertible {
  init(arrayLiteral elements: Element...) {
    self.left = elements.reverse()
    self.right = []
  }
}

現(xiàn)在我們就可以用數(shù)組字面量來創(chuàng)建一個隊列了:

let q: Queue = [1,2,3]

遵守 RangeReplaceableCollectionType

隊列下一個要支持的是 RangeReplaceableCollectionType。這個協(xié)議要求我們做三件事情:

  • reserveCapacity 方法:我們在實現(xiàn) map 的時候已經(jīng)使用過這個方法了窘面。因為最后元素的個數(shù)事前就已經(jīng)知道了貌夕,因此它能夠在數(shù)組申請內(nèi)存時避免不必要的元素復制。一個集合其實在收到 reserveCapacity 調(diào)用時可以什么都不做民镜,忽略掉它并不會造成很大影響啡专。
  • 一個空的初始化方法:在泛型函數(shù)中這會很有用,它可以允許我們創(chuàng)建相同類型的空的集合制圈。
  • replaceRange 函數(shù):它接受一個范圍和一個集合们童,并將原來集合中這個范圍內(nèi)的內(nèi)容用新的集合替換。

RangeReplaceableCollectionType 是展現(xiàn)協(xié)議擴展的絕佳的例子鲸鹦。你通過實現(xiàn)一個超級靈活的 replaceRange 函數(shù)慧库,就能直接由其得到一組引申出的有用的方法:

  • append 和 appendContentsOf:將 endIndex..<endIndex 范圍 (也就是集合末尾的空范圍) 用新的元素進行替換。
  • removeAtIndex 和 removeRange:將 i...i 或者 subRange 的內(nèi)容用空集合進行替換
  • splice 和 insertAtIndex:將 atIndex..<atIndex (也就是在這個位置的空范圍) 用新的值進行替換
  • removeAll:將 startIndex..<endIndex 用一個空集合進行替換

以下是具體的實現(xiàn):

extension Queue: RangeReplaceableCollectionType {
    mutating func reserveCapacity(n: Int) {
      return
    }

    mutating func replaceRange<C: CollectionType where C.Generator.Element == Element>(subRange: Range<Int>, with newElements: C){
      right = left.reverse() + right
      left.removeAll(keepCapacity: true)
      right.replaceRange(subRange, with: newElements)
    }        
}

至此一個自定義的Queue集合就實現(xiàn)并且豐滿完畢了馋嗜。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末齐板,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子葛菇,更是在濱河造成了極大的恐慌甘磨,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件眯停,死亡現(xiàn)場離奇詭異济舆,居然都是意外死亡,警方通過查閱死者的電腦和手機莺债,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進店門滋觉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來签夭,“玉大人,你說我怎么就攤上這事椎侠〉谧猓” “怎么了?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵我纪,是天一觀的道長慎宾。 經(jīng)常有香客問我,道長宣羊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任汰蜘,我火速辦了婚禮仇冯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘族操。我一直安慰自己苛坚,他們只是感情好,可當我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布色难。 她就那樣靜靜地躺著泼舱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪枷莉。 梳的紋絲不亂的頭發(fā)上娇昙,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天,我揣著相機與錄音笤妙,去河邊找鬼冒掌。 笑死,一個胖子當著我的面吹牛蹲盘,可吹牛的內(nèi)容都是我干的股毫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼召衔,長吁一口氣:“原來是場噩夢啊……” “哼铃诬!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起苍凛,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤趣席,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后醇蝴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吩坝,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年哑蔫,在試婚紗的時候發(fā)現(xiàn)自己被綠了钉寝。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弧呐。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖嵌纲,靈堂內(nèi)的尸體忽然破棺而出俘枫,到底是詐尸還是另有隱情,我是刑警寧澤逮走,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布鸠蚪,位于F島的核電站,受9級特大地震影響师溅,放射性物質(zhì)發(fā)生泄漏茅信。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一墓臭、第九天 我趴在偏房一處隱蔽的房頂上張望蘸鲸。 院中可真熱鬧,春花似錦窿锉、人聲如沸酌摇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽窑多。三九已至,卻和暖如春洼滚,著一層夾襖步出監(jiān)牢的瞬間埂息,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工遥巴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留耿芹,地道東北人。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓挪哄,卻偏偏與公主長得像吧秕,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子迹炼,可洞房花燭夜當晚...
    茶點故事閱讀 44,678評論 2 354

推薦閱讀更多精彩內(nèi)容