实现LazyCollectionProtocol

时间:2016-12-30 10:07:12

标签: swift

所以我正在学习有关LazySequenceProtocol和LazyCollectionProtocol的教程。

首先,我尝试了Apple's API reference的示例代码,并且能够实现符合LazyScanSequence的{​​{1}},如下所示:

LazySequenceProtocol

有效。当时我很开心。之后我想进一步扩展到struct LazyScanIterator<Base : IteratorProtocol, ResultElement> : IteratorProtocol { mutating func next() -> ResultElement? { return nextElement.map { result in nextElement = base.next().map { nextPartialResult(result, $0) } return result } } private var nextElement: ResultElement? // The next result of next(). private var base: Base // The underlying iterator. private let nextPartialResult: (ResultElement, Base.Element) -> ResultElement init(nextElement: ResultElement, base: Base, nextPartialResult: @escaping (ResultElement, Base.Element) -> ResultElement) { self.nextElement = nextElement self.base = base self.nextPartialResult = nextPartialResult } } struct LazyScanSequence<Base: Sequence, ResultElement> : LazySequenceProtocol // Chained operations on self are lazy, too { func makeIterator() -> LazyScanIterator<Base.Iterator, ResultElement> { return LazyScanIterator(nextElement: initial, base: base.makeIterator(), nextPartialResult: nextPartialResult) } private let base: Base private let initial: ResultElement private let nextPartialResult: (ResultElement, Base.Iterator.Element) -> ResultElement init(initial: ResultElement, base: Base, nextPartialResult: @escaping (ResultElement, Base.Iterator.Element) -> ResultElement) { self.initial = initial self.base = base self.nextPartialResult = nextPartialResult } } ,以便我可以使用索引来访问结果。

由于Collection继承自LazyCollectionProtocolLazySequenceProtocol,我必须添加更多内容:

Collection

在添加#2 部分之前,我有2个错误说struct LazyScanCollection<Base: Collection, ResultElement> : LazyCollectionProtocol { func makeIterator() -> LazyScanIterator<Base.Iterator, ResultElement> { return LazyScanIterator(nextElement: initial, base: base.makeIterator(), nextPartialResult: nextPartialResult) } private let base: Base private let initial: ResultElement private let nextPartialResult: (ResultElement, Base.Iterator.Element) -> ResultElement init(initial: ResultElement, base: Base, nextPartialResult: @escaping (ResultElement, Base.Iterator.Element) -> ResultElement) { self.initial = initial self.base = base self.nextPartialResult = nextPartialResult } // ^--- #1 conform to Sequence typealias Index = Base.Index var startIndex: Index { return base.startIndex } var endIndex: Index { return base.endIndex } subscript (position: Index) -> Base.Iterator.Element { precondition(indices.contains(position), "out of boundsss") return base[position] } func index(after i: Index) -> Index { return base.index(after: i) } // ^--- #2 conform to IndexableBase }

添加#2 后,即this does not conform to protocol Collection & IndexableBasestartIndexendIndexsubscript,其中一个错误是{{1消失了。但我仍然有一个错误,说这不符合index(after:)

我认为#2 会满足IndexableBase的所有要求,但我想我错了。我不知道如何解决这个问题。

1 个答案:

答案 0 :(得分:2)

问题在于,您LazyScanCollection Iterator.Element的推断类型存在冲突 - 您的makeIterator()方法表明它的类型为LazyScanIterator<Base.Iterator, ResultElement>.Element }(又名。ResultElement),但是您的subscript表示它的类型为Base.Iterator.Element

正确的类型是ResultElement,它是通过迭代每个基本集合的元素,应用nextPartialResult函数获得下一个元素而获得的。但是,您的下标实现不会这样做 - 而是尝试直接下标基础 。这不是你想要的,因为它不会返回扫描的&#39;元素,它只会在扫描之前返回元素。

因此,您需要实现一个下标,该下标返回一个&#39;扫描&#39;集合的给定索引的元素。实现这一点的一种方法是定义自己的索引类型,以便将下一个索引的索引存储到下标,以及当前累积的索引。元件。例如:

// An underlying index for a LazyScanCollection. Note that this will be invalidated
// if the base is mutated.
struct _LazyScanCollectionIndex<BaseIndex : Comparable, ResultElement> : Comparable {

    var nextBaseIndex: BaseIndex // the next index of base to access.
    var element: ResultElement // the currently 'accumulated' element.

    static func ==(lhs: _LazyScanCollectionIndex, rhs: _LazyScanCollectionIndex) -> Bool {
        return lhs.nextBaseIndex == rhs.nextBaseIndex
    }

    static func <(lhs: _LazyScanCollectionIndex, rhs: _LazyScanCollectionIndex) -> Bool {
        return lhs.nextBaseIndex < rhs.nextBaseIndex
    }
}

然后你可以构建一个enum包装器来定义一个结束索引(这也可以通过使nextBaseIndex的{​​{1}}属性成为可选来实现,但我非常喜欢使用自定义枚举)。例如:

_LazyScanCollectionIndex

然后,您需要调整// A wrapper for the index of a LazyScanCollection enum LazyScanCollectionIndex<BaseIndex : Comparable, ResultElement> : Comparable { // either an actual index, or the endIndex. case index(_LazyScanCollectionIndex<BaseIndex, ResultElement>) case endIndex static func ==(lhs: LazyScanCollectionIndex, rhs: LazyScanCollectionIndex) -> Bool { switch (lhs, rhs) { case (.endIndex, .endIndex): return true case let (.index(lhsIndex), .index(rhsIndex)): return lhsIndex == rhsIndex default: return false } } static func <(lhs: LazyScanCollectionIndex, rhs: LazyScanCollectionIndex) -> Bool { switch (lhs, rhs) { case (.index, .endIndex): // endIndex is greater than all indices. return true case let (.index(lhsIndex), .index(rhsIndex)): return lhsIndex < rhsIndex default: return false } } } 的实现以使用此新索引类型:

LazyScanCollection

然后最后通过访问索引的typealias Index = LazyScanCollectionIndex<Base.Index, ResultElement> var startIndex: Index { return .index(_LazyScanCollectionIndex(nextBaseIndex: base.startIndex, element: initial)) } var endIndex: Index { return .endIndex } func index(after i: Index) -> Index { guard case var .index(index) = i else { fatalError("Cannot advance past endIndex") } if index.nextBaseIndex < base.endIndex { // form the next partial result from the next index of the base and the // currently 'accumulated' element. index.element = nextPartialResult(index.element, base[index.nextBaseIndex]) base.formIndex(after: &index.nextBaseIndex) // increment next index. return .index(index) } else { return .endIndex } } 属性来实现您的下标:

element

由于集合中元素的值取决于之前所有元素的值,因此索引以线性时间进行。如果您需要O(1)索引,则应该急切地执行subscript (position: Index) -> ResultElement { // ensure that the index is valid before subscripting. guard case let .index(index) = position, indices.contains(position) else { fatalError("Index \(position) out of bounds.") } return index.element } 方法以生成数组而不是使用延迟集合。