Swift中的集合类型协议

集合类型协议

1 Sequence协议

Sequence 协议是集合类型结构中的基础。一个序列 (sequence) 代表的是一系列具有相同类型的值,你可以对这些值进行迭代。 Sequence 协议提供了许多强大的功能,满足该协议的类型都可以直接使用这些功能。 如下是一个自定义实现Sequence的例子,Sequence协议提供很多功能,其中必须实现的只有一个迭代器,返回一个IteratorProtocol迭代器即可,所有关于Sequence的遍历筛选变形都是通过迭代器来实现的. 要实现自己的迭代器只需返回遵守IteratorProtocol协议的一个对象或者结构体即可.

//Sequence协议必须实现的部分
protocol Sequence {
    associatedtype Iterator: IteratorProtocol
    func makeIterator() -> Iterator
}

//菲波那切数列的Sequence实现方式
struct FibsQueue: Sequence {
    let number: Int
    init(_ number: Int) {
        self.number = number
    }
    typealias Iterator = FibsIterator
    func makeIterator() -> FibsQueue.Iterator {
        return FibsIterator(number)
    }
}

2 IteratorProtocol协议

列通过创建一个迭代器来提供对元素的访问。迭代器每次产生一个序列的值,并且当遍历序列时对遍历状态进行管理。在 IteratorProtocol 协议中唯一的一个方法是 next(),这个方法需要在每次被调用时返回序列中的下一个值。当序列被耗尽时,next() 应该返回 nil.

//
“protocol IteratorProtocol {
    associatedtype Element
    mutating func next() -> Element?
}

/// “FibsIterator 迭代器可以产生一个斐波那契序列。它将记录接下来的两个数字,并作为状态存储,next 函数做的事情是为接下来的调用更新这个状态,并且返回第一个数。和之前的例子一样,这个迭代器也将产生“无穷”的数字,它将持续累加数字”
struct FibsIterator: IteratorProtocol {

    let number: Int
    var index: Int = 0

    init(_ number: Int) {
        self.number = number
    }

    var state = (0,1)
    mutating func next() -> Int? {

        if index >= number {
            return nil
        }
        index += 1

        let fibNumber = state.0
        state = (state.1,state.0+state.1)
        return fibNumber
    }
    typealias Element = Int
}

3 Collection协议

集合类型 (Collection) 指的是那些稳定的序列,它们能够被多次遍历且保持一致。除了线性遍历以外,集合中的元素也可以通过下标索引的方式被获取到。下标索引通常是整数,至少在数组中是这样。不过我们马上回看到,索引也可以是一些不透明值 (比如在字典或者字符串中),这有时候让使用起来不那么直观。集合的索引值可以构成一个有限的范围,它具有定义好了的开始和结束索引。也就是说,和序列不同,集合类型不能是无限的。

Collection协议是建立在Sequence协议上的. 除了从Sequence中集成了全部的方法以外,得益于可以获取指定位置的元素以及稳定迭代的保证,集合还获取了一些新的能力。比如 count 属性,如果序列是不稳定的,那么对序列计数将会消耗序列中的元素,这显然不是我们的目的。但是对于稳定的集合类型,我们就可以对其进行计数。

即使你用不到集合类型提供的这些特性,你依旧可以让你自己的序列满足 Collection 协议,这将告诉你的序列类型的使用者该序列是有限的,而且可以进行多次迭代。不过如果你只是想说明序列可以被多次迭代,但却必须去选一个合适的索引类型,确实会显得比较奇怪。在实现 Collection 协议时,最难的部分在于选取一个合适的索引类型来表达集合类型中的位置。这样设计的一个目的是,Swift 团队希望避免引入一个专门的可多次迭代序列的协议,因为它和 Sequence 拥有同样的要求,但是语义却不一致,这容易让用户感到迷惑。

//Collection协议,swift3,其中房噶和变量还是比较多的,但是很多都有了认的实现,所以我们只需要实现少数几个即可
protocol Collection: Indexable,Sequence {
    associatedtype Iterator: IteratorProtocol = IndexingIterator<Self>
    associatedtype SubSequence: IndexableBase,Sequence = Slice<Self>
    associatedtype Indexdistance: SignedInteger = Int
    associatedtype Indices: IndexableBase,Sequence = DefaultIndices<Self>
    
    var first: Iterator.Element? { get }
    var indices: Indices { get }
    var isEmpty: Bool { get }
    var count: Indexdistance { get }
    
    func makeIterator() -> Iterator
    func prefix(through position: Index) -> SubSequence
    func prefix(upTo end: Index) -> SubSequence
    func suffix(from start: Index) -> SubSequence
    func distance(from start: Index,to end: Index) -> Indexdistance
    func index(_ i: Index,offsetBy n: Indexdistance) -> Index
    func index(_ i: Index,offsetBy n: Indexdistance,limitedBy limit: Index) -> Index?
    
    subscript(position: Index) -> Iterator.Element { get }
    subscript(bounds: Range<Index>) -> SubSequence { get }
}

一个自定义实现FIFO队列的例子

//自定义一个队列
//首先定义好队列到底是什么,我们可以用协议来描述队列是社么
/// 一个能够将元素入队和出队的类型
protocol Queue {
    /// 在 `self` 中所持有的元素的类型
    associatedtype Element
    /// 将 `newElement` 入队到 `self`
    mutating func enqueue(_ newElement: Element)
    /// 从 `self` 出队一个元素
    mutating func dequeue() -> Element?
}

//实现一个队列,FIFO队列,其中元素类型是`Element`
struct FIFOQueue<Element>: Queue {
    fileprivate var left: [Element] = []
    fileprivate var right: [Element] = []
    /// 将元素添加到队列最后
    /// - 复杂度: O(1)
    mutating func enqueue(_ newElement: Element) {
        right.append(newElement)
    }
    /// 从队列前端移除一个元素
    /// 当队列为空时,返回 nil
    /// - 复杂度: 平摊 O(1)
    mutating func dequeue() -> Element? {
        if left.isEmpty {
            left = right.reversed()
            right.removeAll()
        }
        return left.popLast()
    }
}

//遵守 Collection 协议
extension FIFOQueue: Collection {
    public var startIndex: Int { return 0 }
    public var endindex: Int { return left.count + right.count }
    public func index(after i: Int) -> Int {
        precondition(i < endindex)
        return i + 1
    }
    public subscript(position: Int) -> Element {
        precondition((0..<endindex).contains(position),"Index out of bounds")
        if position < left.endindex {
            return left[left.count - position - 1]
        } else {
            return right[position - left.count]
        }
    }
}

//ExpressibleByArrayLiteral协议
extension FIFOQueue: ExpressibleByArrayLiteral {
    public init(arrayLiteral elements: Element...) {
        self.init(left: elements.reversed(),right: [])
    }
}

4 ExpressibleByArrayLiteral协议

当实现一个类似队列这样的集合类型时,最好也去实现一下 ExpressibleByArrayLiteral。这可以让用户能够以他们所熟知的 [value1,value2,etc] 语法创建一个队列。

extension FIFOQueue: ExpressibleByArrayLiteral {
    public init(arrayLiteral elements: Element...) {
        self.init(left: elements.reversed(),right: [])
    }
}

'Self' is only available in a protocol or as the result of a method in a class; ...

Reference: Swift进阶

相关文章

软件简介:蓝湖辅助工具,减少移动端开发中控件属性的复制和粘...
现实生活中,我们听到的声音都是时间连续的,我们称为这种信...
前言最近在B站上看到一个漂亮的仙女姐姐跳舞视频,循环看了亿...
【Android App】实战项目之仿抖音的短视频分享App(附源码和...
前言这一篇博客应该是我花时间最多的一次了,从2022年1月底至...
因为我既对接过session、cookie,也对接过JWT,今年因为工作...