[swift 進階]讀書筆記-第九章:泛型 C9P2 對集合採用泛型操做

第九章:泛型 Generics

9.2 對集合採用泛型操做 Operating Generically on Collections

本小節主要是經過泛型二分法序列隨機排列判斷子序列的位置的算法的幾個Demo,來說了一些應該注意的知識點。git

本小節代碼量有些多,不要求所有掌握。通讀一遍便可,之後在實際開發中有遇到算法相關的泛型封裝能夠回來看看~github

二分法查找 A Binary Search

書中先對RandomAccessCollection協議隨機存取協議(筆記第三章第五節有講,忘了的同窗能夠回頭看看) 封裝一個二分法的extension方法,算法

extension RandomAccessCollection where Index == Int, IndexDistance == Int{只知足Int的二分查找
        public func binarySearch(for value: Iterator.Element,
                                 areInIncreasingOrder: (Iterator.Element, Iterator.Element) -> Bool)
            -> Index? {
                var left = 0
                var right = count - 1
                
                while left <= right {
                    let mid = (left + right) / 2
                    let candidate = self[mid]
                    
                    if areInIncreasingOrder(candidate, value) {
                        left = mid + 1
                    }else if areInIncreasingOrder(value, candidate) {
                        right = mid - 1
                    }else {只有左右兩個相等纔會返回中間值
                        return mid
                    }
                }
                return nil
        }
    }
    let binaryInt = [1,3,2,6,4]
    let bin = binaryInt[1..<3]
    bin.binarySearch(for: 2, areInIncreasingOrder: <)
    print(binaryInt ?? 000)
複製代碼

但會引入一個嚴重的bug,並非全部的集合都以整數爲索引的,Dictionary、Set、String都有本身的索引類型。
還有一個是以整數爲索引的並不必定都以0開始 例如 Array[3..<5] 的startIndex就是3 ,若是使用就會在運行時崩潰。 爲此咱們進行二次修改dom

泛型二分查找 Generic Binary Search

咱們二次修改上面的方法,來知足泛型的要求優化

泛型二分查找
    extension RandomAccessCollection {
        public func binarySearch(for value: Iterator.Element,
                                 areInIncreasingOrder: (Iterator.Element, Iterator.Element) -> Bool)
            -> Index? {
                guard !isEmpty else {
                    return nil
                }
                / left和right再也不是整數類型了,而是對於的索引值
                var left = startIndex
                var right = index(before: endIndex)
                while left <= right {
                    let dist = distance(from: left, to: right)  計算left到right的距離
                    let mid = index(left, offsetBy: dist/2)
                    let candidate = self[mid]
                    
                    if areInIncreasingOrder(candidate, value) {
                        left = index(after: mid)
                    }else if areInIncreasingOrder(value, candidate) {
                        right = index(before: mid)
                    }else {
                        return mid
                    }
                }
                return nil
        }
    }
    extension RandomAccessCollection where Iterator.Element: Comparable {
        func binarySearch(for value: Iterator.Element) -> Index? {
            return binarySearch(for: value, areInIncreasingOrder: <)
        }
    }
    
    let a = ["a", "c", "d", "g"]
    let r = a.reversed()
    r.binarySearch(for: "d", areInIncreasingOrder: >) == r.startIndex
    let s = a[1..<3]
    s.startIndex
    s.binarySearch(for: "d")
複製代碼

這樣對二分法的泛型改造就結束啦~spa

集合隨機排列 Shuffing Collections

咱們先用代碼實現一下 Fisher-Yates洗牌算法。code

/集合隨機排列
    extension Array {
        mutating func shuffle(){
            for i in 0..<(count - 1) {
                let j = Int(arc4random_uniform(UInt32(count - i))) + i
                guard i != j else {保證不會將一個元素與本身交換
                    continue
                }
                
                self.swapAt(i, j)將兩個元素交換
            }
        }
        
        func shuffled() -> [Element] {不可變版本
            var clone = self
            clone.shuffle()
            return clone
        }
    }
複製代碼

和前面的二分法算法同樣,這裏仍是依賴整數索引才能使用。 咱們用泛型進行二次改造orm

extension MutableCollection where Self: RandomAccessCollection {
       mutating func shuffle() {
            var i = startIndex
            let beforeEndIndex = index(before: endIndex)
            while i < beforeEndIndex {
                let dist = distance(from: i, to: endIndex)
                let randomDistance = IndexDistance.distance(dist)
                let j = index(i, offsetBy: randomDistance)
                guard i !=  else {
                    continue
                }
                swap(&self[i], &self[j]
                formIndex(after: &i))
            }
        }
    }

    ///二次封裝
    extension Sequence {
        func shuffled() -> [Iterator.Element] {
            var clone = Array(self)
            clone.shuffle()
            return clone
        }
    }
複製代碼

Subsequence和泛型算法 SubSequence and Generic Algorithms

這一小結最後一個Demo。。求子集合的位置。索引

extension Collection where Iterator.Element: Equatable {
    func search<Other: Sequence>(for pattern: Other) -> Index?
       where Other.Iterator.Element == Iterator.Element {
        return indices.first(where: { (idx) -> Bool in
            suffix(from: idx).starts(with: pattern)
        })
    }
}
let text = "it was the best of times"
text.search(for: ["w","a","s"])
複製代碼

注:若是知道被搜索的序列和待搜索的序列都知足隨機存取(RandomAccessCollection)的索引,咱們能夠二次優化。ip

extension RandomAccessCollection
        where Iterator.Element: Equatable,
            Indices.Iterator.Element == Index,
            SubSequence.Iterator.Element == Iterator.Element,
            SubSequence.Indices.Iterator.Element == Index {
        func search<Other: RandomAccessCollection>(for pattern: Other) -> Index?
        where Other.IndexDistance == IndexDistance,
            Other.Iterator.Element == Iterator.Element{
                ///若是匹配集合比原集合長 直接退出
                guard !isEmpty && pattern.count <= count else {
                    return nil
                }
                ///不然能夠取到容納匹配模式的最後一個索引。
                let stopSearchIndex = index(endIndex, offsetBy: -pattern.count)
                ///檢查從當前位置切片是否能夠以匹配模式開頭。
                return prefix(upTo: stopSearchIndex).indices.first(where: { (idx) -> Bool in
                    suffix(from: idx).starts(with: pattern)
                })
        }
    }
    let numbers = 1..<100
    numbers.search(for: 80..<90)    
複製代碼

overover~

文章源文件地址,你們若是有更好的想法和觀點歡迎交流

相關文章
相關標籤/搜索