所以我正在学习有关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
继承自LazyCollectionProtocol
和LazySequenceProtocol
,我必须添加更多内容:
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 & IndexableBase
,startIndex
,endIndex
和subscript
,其中一个错误是{{1消失了。但我仍然有一个错误,说这不符合index(after:)
。
我认为#2 会满足IndexableBase
的所有要求,但我想我错了。我不知道如何解决这个问题。
答案 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
}
方法以生成数组而不是使用延迟集合。