I've found it remarkably difficult to find "working documentation" for using Sequence / IteratorProtocol in Swift 3. The few tutorials/articles out there seem to be for older Swift.
Imagine a toy doubly-linked list class called DLList ...
public class Node
{
// whatever "thing" you have a group of, this is that "thing"
}
public class DLList
{
// toy linked list class here
// so this is a group of "node" in this example
}
I believe the following represents the simplest (?), correct, way to make it that you can, in a word, use DLList in a for structure.
public class DLList:Sequence
{
// toy linked list class here
public func makeIterator() -> DLListIterator
{
return DLListIterator(self)
}
}
It would seem that all you have to do is add the makeIterator call.
IteratorProtocol
Since the class is DLList, we'll call it DLListIterator. It would seem that
1, you have to have an "init" which basically takes the group class in question
2, you have to have a next call, which must return one of the "things" which are magically related to your group class.
public class DLListIterator:IteratorProtocol
{
var dll:DLList // you must know the group in question
var pointer:Node? // you must know where you are
init(_ dll:DLList)
{
// so note those two items
self.dll = dll
self.pointer = dll.firstOne
}
public func next() -> Node?
{
// return the next one; careful to return nil at end.
let thisOne = self.pointer
self.pointer = self.pointer?.nextOne
return thisOne
}
}
This does seem to work perfectly. ie, you can now go
var d:DLList = DLList()
for n in d
{
print or whatever n
}
you can use e = d.filter( {d.item blah} ) and so on - great.
Question - there's a lot of talk about associated types. In part 1, do you somehow explicitly state/add the "associated type"? Even if it's not explicitly required how would you do that explicitly? What the hell is this associated type business?
Question - in part two I'm completely mystified how it "knows" that Node is the "thing" that relates to DLList. Is there a way to make that explicit, or what am I not understanding?
Swiftness Moreover, the whole thing seems not very Swifty. It seems incredible to do all that just to add iterator output. In Swift3 is there a snappier way, for a real class? (Not a silly example like "countdown numbers".)
Final question I cheerfully mention that the above now allows for and .filter. In fact, is my example "complete" - can I now do everything "iterator-wise" with DLList, that one can do normally in Swift - have I perhaps "forgotten some features" or ?? Is there more to do in making DLList a really good iterator?
It all nicely works via type inference (really strong thing in Swift).
E.g. IteratorProtocol has only one requirement, that is next() -> Element? method. Here is what you can see if you just Cmd-click on IteratorProtocol in XCode:
public protocol IteratorProtocol {
associatedtype Element
public mutating func next() -> Self.Element?
}
So if you declare a type to conform to IteratorProtocol and provide an implementation of some next() -> Foo? then Swift immediately infers that Foo must be an Element.
You can, of course, make an explicit declaration via:
public class DLListIterator: IteratorProtocol {
public typealias Element = Node
public func next() -> Element? {
// ...
}
}
And, yes, once you implemented both (Sequence and Iterator, that is) you can do everything that other Sequences can do. All this thanks to default protocol implementations.
Whether all this boilerplate that in order to conform to Sequence you need to provide makeIterator(), that in turn has to provide a next(), is Swifty or not.. I think this is something more of opinion-based. Sometimes, you can implement Sequence without going down to implementing IteratorProtocol (e.g. when you implement a wrapper). So, the split does make sense to me.
this is my min example
class TestIter: Sequence, IteratorProtocol {
var mylist:[Int] = [1,2,3,4,5,6] // contents
var curPos = 0 // var for iterator
func makeIterator() -> TestIter {
curPos = 0
return self
}
public typealias Element = Int
func next() -> Element? {
if curPos < mylist.count {
let oldPos = curPos
curPos += 1
return mylist[oldPos]
}
return nil
}
}
let testIt = TestIter()
for i in testIt {
print("i:\(i)")
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With