Adopting CollectionType (Collection) in Swift - swift

I'm writing a graphics library to display data in a graph. Since most of the projects I do tend to have a large learning component in them, I decided to create a generically typed struct to manage my data set DataSet<T: Plottable> (note here that Plottable is also Comparable).
In trying to conform to MutableCollectionType, I've run across an error. I'd like to use the default implementation of sort(), but the compiler is giving the following error when trying to use the sorting function.
Ambiguous reference to member 'sort()'
Here's a code example:
var data = DataSet<Int>(elements: [1,2,3,4])
data.sort() //Ambiguous reference to member 'sort()'
The compiler suggests two candidates, but will not actually display them to me. Note that the compiler error goes away if I explicitly implement sort() on my struct.
But the bigger question remains for me. What am I not seeing that I expect the default implementation to be providing? Or am I running across a bug in Swift 3 (this rarely is the case... usually I have overlooked something).
Here's the balance of the struct:
struct DataSet<T: Plottable>: MutableCollection, BidirectionalCollection {
typealias Element = T
typealias Iterator = DataSetIterator<T>
typealias Index = Int
/**
The list of elements in the data set. Private.
*/
private var elements: [Element] = []
/**
Initalize the data set with an array of data.
*/
init(elements data: [T] = []) {
self.elements = data
}
//MARK: Sequence Protocol
func makeIterator() -> DataSetIterator<T> {
return DataSetIterator(self)
}
//MARK: Collection Protocol
subscript(_ index:DataSet<T>.Index) -> DataSet<T>.Iterator.Element {
set {
elements[index] = newValue
}
get {
return elements[index]
}
}
subscript(_ inRange:Range<DataSet<T>.Index>) -> DataSet<T> {
set {
elements.replaceSubrange(inRange, with: newValue)
}
get {
return DataSet<T>(elements: Array(elements[inRange]))
}
}
//required index for MutableCollection and BidirectionalCollection
var endIndex: Int {
return elements.count
}
var startIndex: Int {
return 0
}
func index(after i: Int) -> Int {
return i+1
}
func index(before i: Int) -> Int {
return i-1
}
mutating func append(_ newElement: T) {
elements.append(newElement)
}
// /**
// Sorts the elements of the DataSet from lowest value to highest value.
// Commented because I'd like to use the default implementation.
// - note: This is equivalent to calling `sort(by: { $0 < $1 })`
// */
// mutating func sort() {
// self.sort(by: { $0 < $1 })
// }
//
// /**
// Sorts the elements of the DataSet by an abritrary block.
// */
// mutating func sort(by areInIncreasingOrder: #noescape (T, T) -> Bool) {
// self.elements = self.elements.sorted(by: areInIncreasingOrder)
// }
/**
Returns a `DataSet<T>` with the elements sorted by a provided block.
This is the default implementation `sort()` modified to return `DataSet<T>` rather than `Array<T>`.
- returns: A sorted `DataSet<T>` by the provided block.
*/
func sorted(by areInIncreasingOrder: #noescape (T, T) -> Bool) -> DataSet<T> {
return DataSet<T>(elements: self.elements.sorted(by: areInIncreasingOrder))
}
func sorted() -> DataSet<T> {
return self.sorted(by: { $0 < $1 })
}
}

Your DataSet is a BidirectionalCollection. The sort() you're trying to use requires a RandomAccessCollection. The most important thing you need to add is an Indicies typealias.
typealias Indices = Array<Element>.Indices
Here's my version of your type:
protocol Plottable: Comparable {}
extension Int: Plottable {}
struct DataSet<Element: Plottable>: MutableCollection, RandomAccessCollection {
private var elements: [Element] = []
typealias Indices = Array<Element>.Indices
init(elements data: [Element] = []) {
self.elements = data
}
var startIndex: Int {
return elements.startIndex
}
var endIndex: Int {
return elements.endIndex
}
func index(after i: Int) -> Int {
return elements.index(after: i)
}
func index(before i: Int) -> Int {
return elements.index(before: i)
}
subscript(position: Int) -> Element {
get {
return elements[position]
}
set {
elements[position] = newValue
}
}
subscript(bounds: Range<Int>) -> DataSet<Element> {
get {
return DataSet(elements: Array(elements[bounds]))
}
set {
elements[bounds] = ArraySlice(newValue.elements)
}
}
}
var data = DataSet(elements: [4,2,3,1])
data.sort()
print(data.elements) // [1,2,3,4]
You don't actually need an Iterator if you don't want one. Swift will give you Sequence automatically if you implement Collection.

Related

how to store away sequence variables with constraints in swift

I wanted to create a "where_non_null" operation that works on any swift sequence - which is easy if you return an array, but obviously that is potentially bad performance wise - because you are forcing the entire sequence to resolve in memory - so I created the following that just goes line by line:
//
// this iterates through the underlying sequence, and returns only the values that are not null
//
public class Not_null_iterator<T> : IteratorProtocol
{
public typealias Element = T
private let next_function : () -> T?
init<T_iterator: IteratorProtocol>( _ source: T_iterator ) where T_iterator.Element == Optional<T>
{
var iterator = source
next_function =
{
while (true)
{
if let next_value = iterator.next()
{
if let not_null_value = next_value
{
return not_null_value
}
}
else
{
return nil
}
}
}
}
public func next() -> T? {
next_function()
}
}
//
// a sequence wrapping an underlying sequence, that removes any nulls as we go through
//
public class Not_null_sequence<T > : Sequence
{
private var iterator_creator : () -> Not_null_iterator<T>
init<T_source_sequence : Sequence >( _ source : T_source_sequence ) where T_source_sequence.Element == Optional<T>
{
iterator_creator =
{
Not_null_iterator(source.makeIterator())
}
}
public func makeIterator() -> Not_null_iterator<T>
{
iterator_creator()
}
}
extension Sequence
{
//
// return only the not null values in the sequence without ever resolving more than one item in memory at one time and remove the optionality on the type
//
func where_not_null<T>() -> Not_null_sequence<T> where Element == Optional<T>
{
return Not_null_sequence( self)
}
}
class Where_not_null_tests : XCTestCase
{
public func test_where_not_null()
{
let source = [1, 2, 3, nil, 4]
let checked : [Int] = Array(source.where_not_null())
XCTAssertEqual([1,2,3,4],checked)
}
}
which works great - however I had to define the next() and make_iterator() functions in the constructor, because I couldn't find any type safe way of putting the source into a class level variable.
Is there a way of doing that?
[and yes, I'm aware swift people prefer camel case]
Rather than just using one generic parameter, you'd need two generic parameters. You can't just constrain one generic parameter to say that it has to be some sequence with an element of some Optional. You need another generic parameter to say what the optional's type is:
class NotNilIterator<T: IteratorProtocol, U>: IteratorProtocol where T.Element == U? {
typealias Element = U
var iterator: T
init(_ source: T) {
iterator = source
}
func next() -> Element? {
// I feel this is clearer what is going on
while true {
switch iterator.next() {
case .some(.none):
continue
case .none:
return nil
case .some(.some(let element)):
return element
}
}
}
}
class NotNilSequence<T: Sequence, U> : Sequence where T.Element == U?
{
let sequence: T
init(_ source : T)
{
sequence = source
}
public func makeIterator() -> NotNilIterator<T.Iterator, U>
{
.init(sequence.makeIterator())
}
}
whereNotNil would then be declared like this:
func whereNotNil<T>() -> NotNilSequence<Self, T> where Self.Element == T?
{
return .init(self)
}
Note the use of self types. The first parameter is the type of the underlying sequence, the second is the non-optional type.
Note that this sort of "lazily computed sequence" is already built into Swift. To lazily filter out the nils, do:
let array = [1, 2, 3, nil, 4]
let arrayWithoutNil = array.lazy.compactMap { $0 }
The downside is that the type names are quite long. arrayWithoutNil is of type
LazyMapSequence<LazyFilterSequence<LazyMapSequence<LazySequence<[Int?]>.Elements, Int?>>, Int>
But you can indeed get non-optional Ints out of it, so it does work.
The way swift generics work can sometimes be very confusing (but has it's advantages). Instead of declaring that a variable is of a generic protocol (resp. a protocol with associated types), you instead declare another generic type which itself conforms to your protocol. Here's your iterator as an example (I have taken the liberty to clean up the code a bit):
public class Not_null_iterator<T, T_iterator> : IteratorProtocol where
T_iterator: IteratorProtocol,
T_iterator.Element == Optional<T>
{
private var source: T_iterator
init(_ source: T_iterator) {
self.source = source
}
public func next() -> T? {
while let next_value = source.next()
{
if let not_null_value = next_value
{
return not_null_value
}
}
return nil
}
}
The non-null sequence works analogous:
public class Not_null_sequence<T, Source>: Sequence where
Source: Sequence,
Source.Element == Optional<T>
{
private var source: Source
init(_ source: Source) {
self.source = source
}
public func makeIterator() -> Not_null_iterator<T, Source.Iterator> {
Not_null_iterator(self.source.makeIterator())
}
}
Using this some IteratorProtocol is just a nice way to let the compiler figure out the type. It is equivalent to saying Not_null_iterator<T, Source.Iterator>
As a (potentially) interesting side-note, to clean up the generic mess even more, you can nest the iterator class inside the Not_null_sequence:
public class Not_null_sequence<T, Source>: Sequence where
Source: Sequence,
Source.Element == Optional<T>
{
private var source: Source
init(_ source: Source) {
self.source = source
}
public func makeIterator() -> Iterator{
Iterator(self.source.makeIterator())
}
public class Iterator: IteratorProtocol {
private var source: Source.Iterator
init(_ source: Source.Iterator) {
self.source = source
}
public func next() -> T? {
while let next_value = source.next()
{
if let not_null_value = next_value
{
return not_null_value
}
}
return nil
}
}
}

Is it possible to have a same collection instance in a dictionary associated with multiple keys in swift?

I have a Set instance and want to put it into a Dictionary, and associate it with multiple keys so I can lookup/modify it in the future.
Following Python code is what I want to achieve in Swift.
s = set()
D = {}
D["a"] = s
D["b"] = s
D["a"].add("Hello")
D["a"].add("World")
print(D["b"]) # getting {"Hello", "World"} back
I tried something like following in Swift.
var s = Set<String>()
var D = Dictionary<String, Set<String>>()
D["a"] = s // copy of s is assigned
D["b"] = s // another copy of s is assigned
D["a"]!.insert("Hello")
D["a"]!.insert("World")
print(D["b"]!) // empty :(
Since collections in Swift hold value semantics, by the time I put a set into a dictionary, new instance is created. Is there any workaround? I know I could use NSMutableSet instead of Swift's Set, but I want to know how I can approach this by using collections with value semantics if possible.
Ah! Now we get to the heart of it. You just want a reference type based on stdlib rather than using the one that Foundation gives you. That's straightforward to implement, if slightly tedious. Just wrap a Set in a class. If you don't want full SetAlgebra or Collection conformance, you don't have to implement all of these methods. (And you might want some more init methods to make this more convenient, but hopefully those implementations are fairly obvious from your code needs.)
final class RefSet<Element> where Element: Hashable {
private var storage: Set<Element> = Set()
init() {}
}
extension RefSet: Equatable where Element: Equatable {
static func == (lhs: RefSet<Element>, rhs: RefSet<Element>) -> Bool {
return lhs.storage == rhs.storage
}
}
extension RefSet: SetAlgebra {
var isEmpty: Bool { return storage.isEmpty }
func contains(_ member: Element) -> Bool {
return storage.contains(member)
}
func union(_ other: RefSet<Element>) -> RefSet<Element> {
return RefSet(storage.union(other.storage))
}
func intersection(_ other: RefSet<Element>) -> RefSet<Element> {
return RefSet(storage.intersection(other.storage))
}
func symmetricDifference(_ other: RefSet<Element>) -> RefSet<Element> {
return RefSet(storage.symmetricDifference(other.storage))
}
#discardableResult
func insert(_ newMember: Element) -> (inserted: Bool, memberAfterInsert: Element) {
return storage.insert(newMember)
}
#discardableResult
func remove(_ member: Element) -> Element? {
return storage.remove(member)
}
#discardableResult
func update(with newMember: Element) -> Element? {
return storage.update(with: newMember)
}
func formUnion(_ other: RefSet<Element>) {
storage.formUnion(other.storage)
}
func formIntersection(_ other: RefSet<Element>) {
storage.formIntersection(other.storage)
}
func formSymmetricDifference(_ other: RefSet<Element>) {
storage.formSymmetricDifference(other.storage)
}
}
extension RefSet: Collection {
typealias Index = Set<Element>.Index
var startIndex: Index { return storage.startIndex }
var endIndex: Index { return storage.endIndex }
subscript(position: Index) -> Element {
return storage[position]
}
func index(after i: Index) -> Index {
return storage.index(after: i)
}
}

Implementing LazyCollectionProtocol

So I was following tutorials about LazySequenceProtocol and LazyCollectionProtocol.
First, I tried sample codes from Apple's API reference and was able to implement LazyScanSequence that conforms to LazySequenceProtocol like below:
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
}
}
It worked. I was happy. And after that I wanted to extend it further to Collection, so that I could access the result by using indices.
Since LazyCollectionProtocol is inherited from both LazySequenceProtocol and Collection I had to add more stuffs:
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
}
Before adding #2 part, I had 2 errors saying this does not conform to protocol Collection & IndexableBase.
After adding #2, that is startIndex, endIndex, subscript, and index(after:), one of the errors about IndexableBase disappeared. But I still have an error saying that this does not conform to Collection.
I thought #2 will fulfill all requirements for Collection, but I guess I was wrong. I'm not sure how to fix this problem.
The problem is that you have conflicting inferred types for your LazyScanCollection's Iterator.Element – your makeIterator() method says that it's of type LazyScanIterator<Base.Iterator, ResultElement>.Element (aka. ResultElement), however your subscript says that it's of type Base.Iterator.Element.
The correct type is ResultElement, which is obtained by iterating through each of the base collection's elements, applying the nextPartialResult function in order to get the next element. However, your subscript implementation doesn't do this – instead it attempts to subscript the base directly. This isn't what you want, as it wouldn't return the 'scanned' element, it would just return the element prior to scanning.
Therefore you need to implement a subscript which returns a 'scanned' element for a given index of the collection. One way of implementing this would be to define your own index type in order to store the next index of the base to subscript, along with the currently 'accumulated' element. For example:
// 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
}
}
You could then build an enum wrapper in order to define an end index (this could also be implemented by making the nextBaseIndex property of the _LazyScanCollectionIndex an optional, but I quite like using a custom enum). For example:
// 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
}
}
}
You would then want to adapt your LazyScanCollection's implementation to use this new index type:
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
}
}
And then finally implement your subscript by simply accessing the index's element property:
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
}
As the value of an element in the collection depends on the values of all elements prior to it, indexing takes place in linear time. If you require O(1) indexing, you should eagerly perform the scan(_:_:) method in order to generate an array rather than using a lazy collection.

conforming to Sequence and IteratorProtocol in Swift

I am trying to write my own version of IndexingIterator to increase my understanding of Sequence. I haven't assign any type to associatetype Iterator in my struct. However, the complier doesn't complain about that and I get a default implementation of makeIterator.
Following are my codes:
struct __IndexingIterator<Elements: IndexableBase>: Sequence, IteratorProtocol {
mutating func next() -> Elements._Element? {
return nil
}
}
let iterator = __IndexingIterator<[String]>()
// this works and returns an instance of __IndexingIterator<Array<String>>. why?
iterator.makeIterator()
I think there must be some extensions on Sequence which add the default implementation. Thus, I searched it in Sequence.swift and only found this.
extension Sequence where Self.Iterator == Self, Self : IteratorProtocol {
/// Returns an iterator over the elements of this sequence.
public func makeIterator() -> Self {
return self
}
}
I thought it would be like this:
extension Sequence where Self: IteratorProtocol {
typealias Iterator = Self
...
}
Did I miss something or I misunderstood the extension?
It looks like Alexander's answer is correct. Here's a boiled down version, without using Sequence:
protocol MySequence {
associatedtype Iterator: IteratorProtocol
func maakeIterator() -> Iterator
}
extension MySequence where Self.Iterator == Self, Self : IteratorProtocol {
/// Returns an iterator over the elements of this sequence.
func maakeIterator() -> Self {
return self
}
}
struct __IndexingIterator<Element>: MySequence, IteratorProtocol {
mutating func next() -> Element? {
return nil
}
}
let iterator = __IndexingIterator<[String]>()
iterator.maakeIterator()
You can write your own Iterator which confrom to IteratorProtocol first, then write what you need confrom to Sequence.
Make sure that you have to implement requried func.
struct IteratorTest : IteratorProtocol {
typealias Element = Int
var count : Int
init(count :Int) {
self.count = count
}
mutating func next() -> Int? {
if count == 0 {
return nil
}else {
defer {
count -= 1
}
return count;
}
}
}
struct CountDown : Sequence {
typealias Iterator = IteratorTest
func makeIterator() -> IteratorTest {
return IteratorTest.init(count: 10)
}
}
The type alias isn't necessary, because the Element associated type is inferred from your implementation of next().
Here's a simple example:
protocol ResourceProvider {
associatedtype Resoruce
func provide() -> Resoruce;
}
struct StringProvider {
func provide() -> String { // "Resource" inferred to be "String"
return "A string"
}
}

Conforming a new protocol to Sequence with a default makeIterator() implementation

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?
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: " ")
}
Apparently it was a Swift bug: my code compiles fine using Swift 3.1.