Sequence in swift

begin with one quickSort

之前在學(xué)習(xí)時(shí)曾看到swift如此簡(jiǎn)潔地表達(dá)出了快排算法。

extension Array {
    var decompose : (head: Element, tail: [Element])? {
        return (count > 0) ? (self[0], Array(self[1..<count])) : nil
    }
}

func quickSort(input: [Int]) -> [Int] {
    if let (pivot, rest) = input.decompose {
        let lesser = rest.filter { $0 < pivot }
        let greater = rest.filter { $0 >= pivot }
        let a = quickSort(input: lesser) + [pivot]
        let b = a + quickSort(input: greater)
        return b
    } else {
        return []
    }
}

時(shí)雖嘆于其驚艷,但亦惑于其生澀敷硅。其實(shí)我已經(jīng)不打算講解這個(gè)算法了,但不講就沒(méi)法引出主角Sequence哩俭。咳咳拳恋,開(kāi)課凡资!

  • 這段代碼的精髓在于數(shù)據(jù)結(jié)構(gòu)的擴(kuò)展。在extension中定義了一個(gè)可選的返回?cái)?shù)組首元素和剩余元素的屬性decompose讳苦,以此大大減少了代碼量式廷。
  • 第一次見(jiàn)filter的時(shí)候袜爪,只想說(shuō)這是啥玩意兒啊……咳,這是個(gè)過(guò)濾器苔可。查看文檔可知其接收一個(gè)isIncluded閉包參數(shù)同蜻,此閉包用來(lái)判斷 Sequence中每個(gè)元素是否符合條件并返回bool值標(biāo)識(shí)此元素是否要被加入到方法返回值。就這樣依次過(guò)濾出數(shù)組中符合條件的元素么库,并返回一個(gè)這些元素組成的數(shù)組忱反。
  • 最后經(jīng)過(guò)遞歸依次完成排序,再拼接數(shù)組裙顽。

聲明:關(guān)于這段代碼網(wǎng)絡(luò)上有很多版本甘萧,有在Element處使用泛型T酸钦,如下:

var decompose : (head: T, tail: [T])? {...}

泛型T會(huì)在很多類(lèi)型定義中使用欢伏,以此來(lái)接收不定類(lèi)型的值(比如map滋恬,映射返回的值類(lèi)型是不定的)勋拟,在這個(gè)算法里完全沒(méi)有必要醋安。還有直接將三個(gè)數(shù)組同時(shí)拼接的:

return qsortDemo(input: lesser) + [pivot] + qsortDemo(input: greater)

之前是可行的柠辞,但現(xiàn)在貌似會(huì)報(bào)錯(cuò)图毕,暫不深入。

So what's Sequence?

在上例的快排算法中饲常,我們用到了數(shù)組的filter方法,也提到了map方法鲫骗。其實(shí)在這些我們常用的數(shù)組方法背后计济,有一個(gè)堅(jiān)挺的Sequence協(xié)議……這個(gè)協(xié)議與其后的Collection毯侦,Bidirectional Collection饰及,Random Access CollectionRange Replaceable Collection榄棵,Mutable Collection(依次是集合腺怯、雙向集合、隨機(jī)訪(fǎng)問(wèn)集合继找、范圍可替代集合柠并、可變集合協(xié)議)一起定義了所有我們能夠使用的數(shù)組方法缰雇。

序列是記錄了一組元素的列表,可以是無(wú)限或有限的薄霜,且只能迭代一次奈揍。其定義分兩部分:

protocol Sequence {
    //關(guān)聯(lián)類(lèi)型秘通,即遵守IteratorProtocol的Iterator。
    associatedtype Iterator: IteratorProtocol
    //用來(lái)構(gòu)建Iterator的函數(shù)捻浦,返回的Iterator必須與聲明的Iterator類(lèi)型相同
    func makeIterator() -> Iterator
}

關(guān)于其關(guān)聯(lián)類(lèi)型迭代器遵守的協(xié)議有如下定義:

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

IteratorProtcol與序列協(xié)議相似,擁有一個(gè)關(guān)聯(lián)類(lèi)型Element,即迭代器聲明的類(lèi)型(需要迭代的類(lèi)型)凝赛;next函數(shù)返回序列的下一個(gè)元素并對(duì)迭代器本身進(jìn)行修改(mutating)(使迭代器指向下一個(gè)Element)赚楚。

Let's start with LinkedList !

我們以鏈表為例來(lái)探討Sequence協(xié)議俭嘁。我們可以用enum(枚舉)來(lái)定義一個(gè)鏈表:

//indirect關(guān)鍵字意味著我們將要在此類(lèi)型內(nèi)部使用LinkedListNode<T>;
indirect enum LinkedListNode<T> {
    case value(element: T, next: LinkedListNode<T>
    case end
}

現(xiàn)在捕虽,我們得到了一個(gè)鏈表,可以通過(guò)一個(gè)元素來(lái)得到下一個(gè)元素的鏈表。但是惦蚊,我們還不能對(duì)這個(gè)鏈表使用枚舉(enum)、循環(huán)(比如for)昏鹃、映射(map)尚氛、或者過(guò)濾(filter)等功能。

如果我們讓此鏈表遵循sequence協(xié)議的話(huà)洞渤,它就獲取了使用上述功能的資格阅嘶,就像我們?cè)谝晥D控制器里使用表格的協(xié)議來(lái)達(dá)成一些特定的功能一樣。

extension LinkedListNode: Sequence {
    func makeIterator() -> ??? {
        return ???
    }
}

之前我們了解到sequence協(xié)議中有一個(gè)非可選的方法makeIterator,這意味著我們必須在遵循協(xié)議時(shí)實(shí)現(xiàn)這個(gè)方法來(lái)構(gòu)造迭代器讯柔。在這時(shí)抡蛙,我們還不知道需要返回什么關(guān)聯(lián)類(lèi)型,但不用擔(dān)心魂迄,我們會(huì)把這個(gè)問(wèn)題解決粗截。

在實(shí)現(xiàn)sequence協(xié)議時(shí),我們不知道聲明何種迭代器來(lái)作為關(guān)聯(lián)類(lèi)型捣炬,但我們知道這個(gè)關(guān)聯(lián)類(lèi)型與IteratorProtcol息息相關(guān)熊昌。我們必須先實(shí)現(xiàn)這個(gè)協(xié)議看看:

struct ???: IteratorProtocol {

    var current: LinkedListNode<T>

    mutating func next() -> T? {
        switch current {
        case let .value(element, next):
            current = next
            return element
        case .end:
            return nil
        }
    }
}

這個(gè)時(shí)候,我們已知的就是IteratorProtcol需要關(guān)聯(lián)鏈表中的元素類(lèi)別湿酸,以及我們會(huì)使用next來(lái)返回鏈表中下一個(gè)這種類(lèi)別的元素婿屹。由于我們并不知道我們需要什么類(lèi)別的元素,所以必須使用泛型T來(lái)構(gòu)建這個(gè)自由的選擇空間推溃。與此同時(shí)昂利,我們需要實(shí)現(xiàn)協(xié)議中的非可選方法next,我們知道鏈表可以只有一項(xiàng)或有很多項(xiàng)铁坎,這就意味著next需要返回可選的T蜂奸。

這時(shí)我們需要關(guān)注的就是???。它是Sequence協(xié)議需要的關(guān)聯(lián)類(lèi)型厢呵,也是需要遵循IteratorProtocol的一個(gè)迭代器窝撵。我們使用何種類(lèi)型的迭代器來(lái)完成迭代操作,就意味著我們需要實(shí)現(xiàn)哪種類(lèi)型的迭代襟铭。鏈表迭代器(LinkedListIterator碌奉,因?yàn)槲覀冊(cè)谀面湵碜鰧?shí)驗(yàn))正是我們需要的啊。但是單迭代器的返回是沒(méi)有意義的寒砖,我們需要知道它將迭代何種類(lèi)型赐劣,很顯然我們不知道(因?yàn)槟鞘擎湵肀粚?shí)例化后才可以推斷出的),所以使用泛型T哩都。于是我們可以得出???-->LinkedListIterator<T>魁兼。即完整的實(shí)現(xiàn)sequence協(xié)議需要以下代碼:

indirect enum LinkedListNode<T> {
    case value(element: T, next: LinkedListNode<T>
    case end
}
  
extension LinkedListNode: Sequence {
    func makeIterator() -> LinkedListIterator<T> {
        //returns current
        return LinkedListIterator(current: self)
    }
}

struct LinkedListIterator<T>: IteratorProtocol {
    //用于指出迭代器當(dāng)前處于序列何處的狀態(tài)變量,它指向鏈表當(dāng)前節(jié)點(diǎn)漠嵌。
    var current: LinkedListNode<T>
    //實(shí)現(xiàn)next需要的功能咐汞。
    mutating func next() -> T? {
        switch current {
        case let .value(element, next):
            current = next
            return element
        case .end:
            return nil
        }
    }
}

就這樣,我們已經(jīng)得到了一個(gè)遵循著Sequence協(xié)議的鏈表∪迓梗現(xiàn)在你可以使用for loop化撕、filter或者map...(很多功能,自己腦補(bǔ)吧)...來(lái)操作這個(gè)鏈表约炎。

See this mysterious iterator

上面我們已經(jīng)定義了一個(gè)LinkedListNode<T>類(lèi)型的鏈表植阴。我們通過(guò)實(shí)例化一個(gè)存放三個(gè)元素A,B,C的鏈表來(lái)探討迭代器Iterator

let linkedList = LinkedListNode<String>.value(element: "A", next: secondNode)
let secondNode = LinkedListNode<String>.value(element: "B", next: thirdNode)
let thirdNode = LinkedListNode<String>.value(element: "C", next: LinkedListNode<String>.end)

當(dāng)然蟹瘾,下面這種形式也能創(chuàng)建同樣的鏈表,如果你喜歡這樣寫(xiě)的話(huà)(雖然這樣看起來(lái)更有鏈表的樣子):

let linkedList = LinkedListNode<String>.value(element: "A", next: LinkedListNode<String>.value(element: "B", next: LinkedListNode<String>.value(element: "C", next: LinkedListNode<String>.end)))

現(xiàn)在我們就可以用linkedList來(lái)創(chuàng)建一個(gè)Iterator了:

var iterator = linkedList.makeIterator();

由上面遵循的sequence協(xié)議可知掠手,我們創(chuàng)建出的Iterator包含有指向鏈表頭的指針憾朴,即current變量。我們可以通過(guò)調(diào)用iterator.next來(lái)返回鏈表首元素A喷鸽,這時(shí)众雷,current會(huì)通過(guò)IteratorProtocol更新以指向下一個(gè)元素:

var element: Any?
repeat {
     //把當(dāng)前迭代器指向的元素賦值給element。
     element = iterator.next()
     //repeat依次打印出A魁衙、B报腔、C
     if let _ = element
     {print(element!)}
} while ((element as? String) != nil)

Add functions to our LinkedList

如果我們需要某些操作,比如查詢(xún)鏈表中符合某條件元素的個(gè)數(shù)剖淀。這很簡(jiǎn)單,使用filter選出元素組成數(shù)組纤房,再調(diào)用count計(jì)數(shù):

let numberOfAdmins = users.filter({ $0.isAdmin }).count

我們何不直接擴(kuò)展sequence協(xié)議纵隔,添加count方法,這樣我們就能這樣寫(xiě)了:

//是不是比上面的寫(xiě)法好看多了炮姨。捌刮。。舒岸。
let numberOfAdmins = users.count({ $0.isAdmin })

那我們就嘗試擴(kuò)展一下sequence協(xié)議吧绅作。為了實(shí)現(xiàn)這個(gè)想法,我們需要在extension 中定義一個(gè)count 方法:

extension Sequence {
//我們已經(jīng)知道的是需要返回一個(gè)Int值
    func count(...) -> Int {
        var count = 0
        //邏輯實(shí)現(xiàn)
        return count
    }
}

首先要知道我們希望的形式是users.count({ $0.isAdmin })蛾派,這樣一來(lái)我們可以參考filter的形式(最前面快排算法中提到)俄认,使其接收一個(gè)isIncluded閉包參數(shù)并返回bool值判斷是否加入元素到返回序列。閉包中需要使用序列中的元素洪乍,也就是我們鏈表中的元素眯杏。上面講迭代器時(shí)我們知道可以通過(guò)iterator.element來(lái)訪(fǎng)問(wèn)這些元素。那么壳澳,當(dāng)當(dāng)岂贩!我們的參數(shù)部分就完成了:

func count(_ isIncluded: (Iterator.Element) -> Bool) -> Int {...}

接下來(lái)就只需要處理count,使其返回為符合條件的值個(gè)數(shù)巷波。我們知道filter篩選元素就是通過(guò)接收到的這個(gè)閉包參數(shù)完成的萎津。所以我們需要獲取所有元素,并依次通過(guò)isIncluded來(lái)標(biāo)記它們:

//此處self即是調(diào)用此方法的鏈表
for element in self{
    //滿(mǎn)足我們自己所寫(xiě)條件時(shí)標(biāo)記一次
    if isIncluded(element){
       count += 1
    }
}

到這里我們的count方法已經(jīng)完成抹镊,并且可以調(diào)用了锉屈。全部實(shí)現(xiàn)及調(diào)用如下:

//實(shí)現(xiàn)
extension Sequence{
    
    func count(_ isIncluded:(Iterator.Element) -> Bool) -> Int{
        
        var count = 0
        for element in self{
            print(self)
            if isIncluded(element){
                count += 1
            }
        }
        return count
    }
}
//調(diào)用
let countOfA = linkedList.count({$0 == "A"})
//等價(jià)于
let countOfA = linkedList.filter({$0 == "A"}).count

Happy ending

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末部念,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌儡炼,老刑警劉巖妓湘,帶你破解...
    沈念sama閱讀 211,948評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異乌询,居然都是意外死亡榜贴,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)妹田,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)唬党,“玉大人,你說(shuō)我怎么就攤上這事鬼佣∈还埃” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,490評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵晶衷,是天一觀的道長(zhǎng)蓝纲。 經(jīng)常有香客問(wèn)我,道長(zhǎng)晌纫,這世上最難降的妖魔是什么税迷? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,521評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮锹漱,結(jié)果婚禮上箭养,老公的妹妹穿的比我還像新娘。我一直安慰自己哥牍,他們只是感情好毕泌,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著砂心,像睡著了一般懈词。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上辩诞,一...
    開(kāi)封第一講書(shū)人閱讀 49,842評(píng)論 1 290
  • 那天坎弯,我揣著相機(jī)與錄音,去河邊找鬼译暂。 笑死抠忘,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的外永。 我是一名探鬼主播崎脉,決...
    沈念sama閱讀 38,997評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼伯顶!你這毒婦竟也來(lái)了囚灼?” 一聲冷哼從身側(cè)響起骆膝,我...
    開(kāi)封第一講書(shū)人閱讀 37,741評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎灶体,沒(méi)想到半個(gè)月后阅签,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,203評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蝎抽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評(píng)論 2 327
  • 正文 我和宋清朗相戀三年政钟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片樟结。...
    茶點(diǎn)故事閱讀 38,673評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡养交,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瓢宦,到底是詐尸還是另有隱情碎连,我是刑警寧澤,帶...
    沈念sama閱讀 34,339評(píng)論 4 330
  • 正文 年R本政府宣布刁笙,位于F島的核電站破花,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏疲吸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評(píng)論 3 313
  • 文/蒙蒙 一前鹅、第九天 我趴在偏房一處隱蔽的房頂上張望摘悴。 院中可真熱鬧,春花似錦舰绘、人聲如沸蹂喻。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,770評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)口四。三九已至,卻和暖如春秦陋,著一層夾襖步出監(jiān)牢的瞬間蔓彩,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,000評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工驳概, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赤嚼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,394評(píng)論 2 360
  • 正文 我出身青樓顺又,卻偏偏與公主長(zhǎng)得像更卒,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子稚照,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評(píng)論 2 349

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

  • Swift 語(yǔ)言中提供了一種 for .. in 語(yǔ)法的形式蹂空,用于遍歷集合俯萌,比如對(duì)于 Array 類(lèi)型,就可以用 ...
    樂(lè)視薯片閱讀 335評(píng)論 0 0
  • 從三月份找實(shí)習(xí)到現(xiàn)在上枕,面了一些公司咐熙,掛了不少,但最終還是拿到小米姿骏、百度糖声、阿里、京東分瘦、新浪蘸泻、CVTE、樂(lè)視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,213評(píng)論 11 349
  • 《Kotlin 極簡(jiǎn)教程 》第5章 集合類(lèi) 《Kotlin極簡(jiǎn)教程》正式上架: 點(diǎn)擊這里 > 去京東商城購(gòu)買(mǎi)閱讀 ...
    光劍書(shū)架上的書(shū)閱讀 2,215評(píng)論 0 11
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法嘲玫,類(lèi)相關(guān)的語(yǔ)法悦施,內(nèi)部類(lèi)的語(yǔ)法,繼承相關(guān)的語(yǔ)法去团,異常的語(yǔ)法抡诞,線(xiàn)程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,598評(píng)論 18 399
  • 幾乎無(wú)時(shí)無(wú)刻不在想著某個(gè)人。 睡覺(jué)前在想土陪,起床后在想昼汗, 吃飯時(shí)在想,發(fā)呆時(shí)在想鬼雀, 忙起來(lái)在想顷窒,閑下來(lái)在想, 拿起手...
    保質(zhì)期一天閱讀 335評(píng)論 0 1