Как обнаружить первый запуск IteratorProtocol в Swift?

Попытка обнаружить первый запуск протокола Iterator. В приведенном ниже примере я пытаюсь начать печать серии Фибоначчи с нуля, но она начинается с 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)))

Какие изменения могут быть внесены в приведенный выше код для обнаружения первого запуска?

1 ответ

Решение

Чтобы ответить на ваш дословный вопрос: вы можете добавить логическую переменнуюfirstRun обнаружить первый звонок next() метод:

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]

Но есть более элегантные решения для этой проблемы. Вы можете "отложить" обновление a а также b должно быть сделано после возврата текущего значения:

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]

или - возможно, проще - изменить начальные значения (используя тот факт, что числа Фибоначчи также определены для отрицательных индексов):

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]

Обратите внимание, что если вы заявляете о соответствии Sequence протокол, то вам не нужно AnySequence обертка (есть реализация по умолчанию makeIterator() для типов, соответствующихIteratorProtocol). Также обычно предпочтительны типы значений, поэтому, если не требуется ссылочная семантика, вы можете сделать это 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()
Другие вопросы по тегам