鉴于基于struct
的通用CollectionType
...
struct MyCollection<Element>: CollectionType, MyProtocol {
typealias Index = MyIndex<MyCollection>
subscript(i: Index) -> Element { … }
func generate() -> IndexingGenerator<MyCollection> {
return IndexingGenerator(self)
}
}
...如何为它定义Index
......
struct MyIndex<Collection: MyProtocol>: BidirectionalIndexType {
func predecessor() -> MyIndex { … }
func successor() -> MyIndex { … }
}
...没有引入死亡的依赖循环?
MyIndex
的一般性质是必要的,因为:
MyProtocol
。MyProtocol
引用Self
因此只能用作类型约束。如果有前向声明(àObjective-C),我会 [sic!] 为MyIndex<MyCollection>
添加一个MyCollection<…>
到indirect enum BinaryTree<Element>: CollectionType, BinaryTreeType {
typealias Index = BinaryTreeIndex<BinaryTree>
case Nil
case Node(BinaryTree, Element, BinaryTree)
subscript(i: Index) -> Element { … }
}
}。唉,没有这样的事情。
可能的具体用例是二叉树,例如:
Index
这需要基于堆栈的struct BinaryTreeIndex<BinaryTree: BinaryTreeType>: BidirectionalIndexType {
let stack: [BinaryTree]
func predecessor() -> BinaryTreeIndex { … }
func successor() -> BinaryTreeIndex { … }
}
:
struct
人们不能(但是?)在Swift中将struct
嵌套在通用BinaryTreeIndex<…>
内。
否则,我只需在BinaryTree<…>
内移动BinaryTreeIndex
。
此外,我更愿意使用一个通用BinaryTreeType
,
然后可以使用任何类型的D3
。
答案 0 :(得分:2)
您无法在结构中嵌套结构,因为它们是值类型。它们不是指向对象的指针,而是将它们的属性保存在变量中。想想结构是否包含自身,它的内存布局是什么样的?
前向声明在Objective-C中工作,因为它们随后用作指针。这就是indirect
关键字被添加到枚举的原因 - 它告诉编译器通过指针添加间接级别。
理论上,相同的关键字可以添加到结构中,但它没有多大意义。你可以手工做indirect
手工做的事情,但是有一个类框:
// turns any type T into a reference type
final class Box<T> {
let unbox: T
init(_ x: T) { unbox = x }
}
你可以用它来装箱一个结构来创建,例如一个链表:
struct ListNode<T> {
var box: Box<(element: T, next: ListNode<T>)>?
func cons(x: T) -> ListNode<T> {
return ListNode(node: Box(element: x, next: self))
}
init() { box = nil }
init(node: Box<(element: T, next: ListNode<T>)>?)
{ box = node }
}
let nodes = ListNode().cons(1).cons(2).cons(3)
nodes.box?.unbox.element // first element
nodes.box?.unbox.next.box?.unbox.element // second element
您可以将此节点直接转换为集合,方法是将其与ForwardIndexType
和CollectionType
相符,但这不是一个好主意。
例如,他们需要非常不同的==
实现:
Equatable
的元素。 集合需要比较两个不同的集合,以查看它们是否包含相同的元素。它 需要元素符合Equatable
,即:
func == <T where T: Equatable>(lhs: List<T>, rhs: List<T>) -> Bool {
// once the List conforms to at least SequenceType:
return lhs.elementsEqual(rhs)
}
最好将其包装在两种特定类型中。这是“免费的” - 包装器没有开销,只是帮助您更轻松地构建正确的行为:
struct ListIndex<T>: ForwardIndexType {
let node: ListNode<T>
func successor() -> ListIndex<T> {
guard let next = node.box?.unbox.next
else { fatalError("attempt to advance past end") }
return ListIndex(node: next)
}
}
func == <T>(lhs: ListIndex<T>, rhs: ListIndex<T>) -> Bool {
switch (lhs.node.box, rhs.node.box) {
case (nil,nil): return true
case (_?,nil),(nil,_?): return false
case let (x,y): return x === y
}
}
struct List<T>: CollectionType {
typealias Index = ListIndex<T>
var startIndex: Index
var endIndex: Index { return ListIndex(node: ListNode()) }
subscript(idx: Index) -> T {
guard let element = idx.node.box?.unbox.element
else { fatalError("index out of bounds") }
return element
}
}
(无需实现generate()
- 通过实施CollectionType
,您可以在2.0中免费获得索引生成器
您现在拥有一个功能齐全的集合:
// in practice you would add methods to List such as
// conforming to ArrayLiteralConvertible or init from
// another sequence
let list = List(startIndex: ListIndex(node: nodes))
list.first // 3
for x in list { print(x) } // prints 3 2 1
现在所有这些代码看起来都很恶心,原因有两个。
一个是因为box妨碍了,而indirect
更好,因为编译器会为你彻底排除它。但它正在做类似的事情。
另一个是结构不是一个很好的解决方案。枚举好多了。实际上代码真的使用枚举 - 这就是Optional。仅代替nil
(即Optional.None
),最好在链表的末尾加上End
个案例。这就是我们正在使用它。
答案 1 :(得分:2)
虽然 Airspeed Velocity 的答案适用于最常见的情况,但我的问题是专门询问推广CollectionType
索引的特殊情况,以便能够分享所有可想象的二叉树的单个Index
实现(其递归性质使得必须使用堆栈进行基于索引的遍历(至少对于没有父指针的树)),这需要{{ 1}}专门针对实际的Index
,而不是BinaryTree
。
我解决此问题的方法是将Element
重命名为MyCollection
,撤销其MyCollectionStorage
符合性,并使用现在取代CollectionType
的结构包装它处理符合MyCollection
。
让事情变得更加真实&#34;我将参考:
CollectionType
为MyCollection<E>
SortedSet<E>
为MyCollectionStorage<E>
BinaryTree<E>
为MyIndex<T>
所以不用多说:
BinaryTreeIndex<T>
这种依赖关系图从有向循环图转变为有向无环图。