【发布时间】:2016-12-24 01:30:24
【问题描述】:
不久前,我在 Swift 中创建了一个二叉搜索树类型,我希望它符合 Collection 协议。但是,endIndex 要求是一个“结束”索引,它并不真正适合树,因为每个索引都应该包含对其对应节点的引用以进行 O(1) 访问。我最终得到了一个可选引用(在 endIndex 的情况下为 nil),但它涉及到很多我宁愿避免的样板代码。
我决定创建一个如下所示的ValidIndexCollection 协议:
/// A collection defined by valid indices only, rather than a
/// startIndex and a "past the end" endIndex.
protocol ValidIndexCollection: Collection {
associatedtype ValidIndex: Comparable
/// The first valid index if the collection is nonempty,
/// nil otherwise.
var firstValidIndex: ValidIndex? { get }
/// The last valid index if the collection is nonempty,
/// nil otherwise.
var lastValidIndex: ValidIndex? { get }
/// Returns the index right after the given index.
func validIndex(after index: ValidIndex) -> ValidIndex
/// Returns the element at the given index.
func element(at index: ValidIndex) -> Iterator.Element
}
在我可以扩展这个协议以满足Collection 的要求之前,我必须先引入一个合适的索引:
enum ValidIndexCollectionIndex<ValidIndex: Comparable> {
case index(ValidIndex)
case endIndex
}
extension ValidIndexCollectionIndex: Comparable {
// ...
}
现在我可以扩展ValidIndexCollection:
// Implementing the Collection protocol requirements.
extension ValidIndexCollection {
typealias _Index = ValidIndexCollectionIndex<ValidIndex>
var startIndex: _Index {
return firstValidIndex.flatMap { .index($0) } ?? .endIndex
}
var endIndex: _Index {
return .endIndex
}
func index(after index: _Index) -> _Index {
guard case .index(let validIndex) = index else { fatalError("cannot increment endIndex") }
return .index(self.validIndex(after: validIndex))
}
subscript(index: _Index) -> Iterator.Element {
guard case .index(let validIndex) = index else { fatalError("cannot subscript using endIndex") }
return element(at: validIndex)
}
}
一切似乎都很好,编译器没有抱怨!但是,我尝试为自定义类型实现此协议:
struct CollectionOfTwo<Element> {
let first, second: Element
}
extension CollectionOfTwo: ValidIndexCollection {
var firstValidIndex: Int? { return 0 }
var lastValidIndex: Int? { return 1 }
func validIndex(after index: Int) -> Int {
return index + 1
}
subscript(index: Int) -> Element {
return index == 0 ? first : second
}
}
现在编译器抱怨CollectionOfTwo 不符合Collection、Sequence 和IndexableBase。错误消息非常无用,主要是以下消息:
协议需要嵌套类型
SubSequence;要添加吗?
或
关联类型
Indices(来自协议Collection)的默认类型DefaultIndices<CollectionOfTwo<Element>>不符合IndexableBase
有什么办法可以使这个工作吗?据我所知,ValidIndexCollection 可以很好地满足Collection 的要求。
注意事项:
我调用了
ValidIndexCollection协议方法validIndex(after:)那样是因为称它为index(after:)尝试实现此协议时导致分段错误。这可能与 来自Collection协议的index(after:)方法。出于同样的原因,我使用
element(at:)而不是下标。我使用了
typealias _Index而不是typealias Index,因为后者导致了一条错误消息,指出“Index在此上下文中的类型查找不明确”。同样,这可能与Collection的Index关联类型有关。
【问题讨论】:
标签: swift protocols swift3 swift-protocols