2

I made a (very basic) BinaryTree protocol:

public enum BinaryTreeChildSide {
    case left, right
}

public protocol BinaryTree {

    associatedtype Element
    associatedtype Index

    func child(of index: Index, side: BinaryTreeChildSide) -> Index?
    var rootIndex: Index? { get }
    subscript(position: Index) -> Element { get }

}

For a basic iterative in-order traversal, I made a BinaryTreeIterator (note that I don't implement Sequence just yet):

public extension BinaryTree {

    func makeIterator() -> BinaryTreeIterator<Self> {
        return BinaryTreeIterator(self)
    }

}

public struct BinaryTreeIterator<Tree: BinaryTree>: IteratorProtocol {

    private let tree: Tree
    private var stack: [Tree.Index]
    private var index: Tree.Index?

    private init(_ tree: Tree) {
        self.tree = tree
        stack = []
        index = tree.rootIndex
    }

    public mutating func next() -> Tree.Element? {
        while let theIndex = index {
            stack.append(theIndex)
            index = tree.child(of: theIndex, side: .left)
        }

        guard let currentIndex = stack.popLast() else { return nil }
        defer { index = tree.child(of: currentIndex, side: .right) }

        return tree[currentIndex]
    }

}

Implementing a binary heap to this protocol is also pretty straight-forward:

public struct BinaryHeap<Element> {

    private var elements: [Element]

    public init(_ elements: [Element]) {
        self.elements = elements
    }

}

extension BinaryHeap: BinaryTree {

    private func safeIndexOrNil(_ index: Int) -> Int? {
        return elements.indices.contains(index) ? index : nil
    }

    public func child(of index: Int, side: BinaryTreeChildSide) -> Int? {
        switch side {
        case .left: return safeIndexOrNil(index * 2 + 1)
        case .right: return safeIndexOrNil(index * 2 + 2)
        }
    }

    public var rootIndex: Int? { return safeIndexOrNil(0) }

    public subscript(position: Int) -> Element {
        return elements[position]
    }

}

So far, so good. I can now make a simple heap and iterate through its elements:

let heap = BinaryHeap([4, 2, 6, 1, 3, 5, 7])
var iterator = heap.makeIterator()

while let next = iterator.next() {
    print(next, terminator: " ")
}
// 1 2 3 4 5 6 7

This works, but of course the goal of implementing makeIterator() is to conform to Sequence. however, if I replace

public protocol BinaryTree {

with

public protocol BinaryTree: Sequence {

then the compiler complains that BinaryHeap doesn't implement Sequence because the associated type Iterator couldn't be inferred. If I manually specify the Iterator type with

extension BinaryHeap: BinaryTree {

    typealias Iterator = BinaryTreeIterator<BinaryHeap>

    ...

}

then the compiler shows an error that Iterator circularly references itself. So that might be why the Iterator type couldn't be inferred.

Interestingly, it works if I wrap my custom BinaryTreeIterator in an AnyIterator instance:

public extension BinaryTree {

    func makeIterator() -> AnyIterator<Element> {
        return AnyIterator(BinaryTreeIterator(self))
    }

}

let heap = BinaryHeap([4, 2, 6, 1, 3, 5, 7])

for number in heap {
    print(number, terminator: " ")
}
// 1 2 3 4 5 6 7

Apple's own IndexingIterator seems to work in a similar fashion to my BinaryTreeIterator:

public struct IndexingIterator<
    Elements : IndexableBase
    // FIXME(compiler limitation):
    // Elements : Collection
> : IteratorProtocol, Sequence {
    ...
}

From the source code. Maybe the problem I'm facing might also be because of the compiler limitation mentioned there, but I don't know for sure.

Is there a way to conform BinaryTree to Sequence without using AnyIterator?

Tim Vermeulen
  • 12,352
  • 9
  • 44
  • 63
  • This is an incredibly vexing problems. So far I've put hours into research and haven't found anything truely useful. – KFDoom Jul 03 '16 at 06:27
  • How about instead of using the `AnyIterator` consider using the BinaryTreeIterator to type cast, and add `Sequence` as a protocol to conform to in the `BinaryTreeIterator` struct? – KFDoom Jul 03 '16 at 08:42
  • @KFDoom I'm not sure I follow, what do you want to type cast? – Tim Vermeulen Jul 03 '16 at 10:41
  • Yes, I suppose this is dreadfully unclear. Thank you your patience. What I mean is the following: https://gist.github.com/anonymous/80f162093f0b6f5354088b3823e503e2 – KFDoom Jul 03 '16 at 10:51
  • We can conform `BinaryTreeIterator` to `Sequence`, but the compiler will still complain about `BinaryHeap` if we try to conform the `BinaryTree` protocol to `Sequence`. :/ – Tim Vermeulen Jul 03 '16 at 12:03
  • Right but in my answer I don't *just* conform to Sequence, I also conform to the IteratorProtocol. When I place the code in a playground it returns results w/o any errors. Would you mind testing it? – KFDoom Jul 04 '16 at 09:21
  • You conform `BinaryTreeIterator` to `Sequence` and `IteratorProtocol`, but you don't conform `BinaryTree` to anything, right? – Tim Vermeulen Jul 04 '16 at 10:15
  • You're right! My mistake. Okay I think I get it now. Mind if we had a quick chat? – KFDoom Jul 04 '16 at 10:52
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/116364/discussion-between-kfdoom-and-tim-vermeulen). – KFDoom Jul 04 '16 at 10:52

2 Answers2

0

This is the farthest I could take it. Now the compiler will still complain about the heap not containing any makeIterator member (which I thought was included by default once someone conforms to sequence—wrong turns out one must conform to Sequence ​and​ IteratorProtocol for the default implementation) and having a next—but once you add those methods its smooth sailing.

So makeIterator + next method to make Mr/Mrs/Preferred Gender Pronoun compiler happy.

public enum BinaryTreeChildSide {
    case left, right
}

public struct BinaryTreeIterator<Tree: BinaryTree>: Sequence, IteratorProtocol {

    private let tree: Tree
    private var stack: [Tree.Index]
    private var index: Tree.Index?

    private init(_ tree: Tree) {
        self.tree = tree
        stack = []
        index = tree.rootIndex
    }

    public mutating func next() -> Tree.Element? {
        while let theIndex = index {
            stack.append(theIndex)
            index = tree.child(of: theIndex, side: .left)
        }

        guard let currentIndex = stack.popLast() else { return nil }
        defer { index = tree.child(of: currentIndex, side: .right) }

        return tree[currentIndex]
    }

}


public protocol BinaryTree: Sequence {

    associatedtype Element
    associatedtype Index

    func child(of index: Index, side: BinaryTreeChildSide) -> Index?
    var rootIndex: Index? { get }
    subscript(position: Index) -> Element { get }

}



extension BinaryTree {

    func makeIterator() -> BinaryTreeIterator<Self> {
        return BinaryTreeIterator(self)
    }

}

extension BinaryHeap {


    private func safeIndexOrNil(_ index: Int) -> Int? {
        return elements.indices.contains(index) ? index : nil
    }

    public func child(of index: Int, side: BinaryTreeChildSide) -> Int? {
        switch side {
        case .left: return safeIndexOrNil(index * 2 + 1)
        case .right: return safeIndexOrNil(index * 2 + 2)
        }
    }

    public var rootIndex: Int? { return safeIndexOrNil(0) }

    public subscript(position: Int) -> Element {
        return elements[position]
    }

}

public struct BinaryHeap<Element> {

    private var elements: [Element]


    public init(_ elements: [Element]) {
        self.elements = elements
    }

}

let heap = BinaryHeap([4, 2, 6, 1, 3, 5, 7])
var iterator = heap.makeIterator()

while let next = iterator.next() {
    print(next, terminator: " ")
}
KFDoom
  • 772
  • 1
  • 6
  • 19
0

Apparently it was a Swift bug: my code compiles fine using Swift 3.1.

Tim Vermeulen
  • 12,352
  • 9
  • 44
  • 63