首页 > 解决方案 > How to detect the first run of an IteratorProtocol in swift?

问题描述

Trying to detect the first run of an Iterator protocol. In the example below I'm trying to start printing Fibonacci series from Zero but it starts from One:

class FibIterator : IteratorProtocol {
var (a, b) = (0, 1)

func next() -> Int? {
    (a, b) = (b, a + b)
    return a
}
}

let fibs = AnySequence{FibIterator()}

print(Array(fibs.prefix(10)))

What modifications can be made to the above code to detect the first run?

标签: iosswiftalgorithmfibonacciiterator-protocol

解决方案


To answer your verbatim question: You can add a boolean variable firstRun to detect the first call of the next() method:

class FibIterator : IteratorProtocol {
    var firstRun = true
    var (a, b) = (0, 1)

    func next() -> Int? {
        if firstRun {
            firstRun = false
            return 0
        }
        (a, b) = (b, a + b)
        return a
    }
}

let fibs = AnySequence { FibIterator() }
print(Array(fibs.prefix(10))) // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

But there are more elegant solutions for this problem. You can “defer” the update of a and b to be done after returning the current value:

class FibIterator : IteratorProtocol {
    var (a, b) = (0, 1)

    func next() -> Int? {
        defer { (a, b) = (b, a + b) }
        return a
    }
}

let fibs = AnySequence { FibIterator() }
print(Array(fibs.prefix(10))) // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

or – perhaps simpler – change the initial values (using the fact that the Fibonacci numbers are defined for negative indices as well):

class FibIterator : IteratorProtocol {
    var (a, b) = (1, 0)  // (Fib(-1), Fib(0))

    func next() -> Int? {
        (a, b) = (b, a + b)
        return a
    }
}

let fibs = AnySequence { FibIterator() }
print(Array(fibs.prefix(10))) // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

Note that if you declare conformance to the Sequence protocol then you don't need the AnySequence wrapper (there is a default implementation of makeIterator() for types conforming to IteratorProtocol). Also value types are generally preferred, so – unless the reference semantics is needed – you can make it a struct:

struct FibSequence : Sequence, IteratorProtocol {
    var (a, b) = (1, 0)  // (Fib(-1), Fib(0))

    mutating func next() -> Int? {
        (a, b) = (b, a + b)
        return a
    }
}

let fibs = FibSequence()

推荐阅读