Как обнаружить первый запуск IteratorProtocol в swift? - PullRequest
0 голосов
/ 30 августа 2018

Попытка обнаружить первый запуск протокола 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 Ответ

0 голосов
/ 30 августа 2018

Чтобы ответить на ваш дословный вопрос: вы можете добавить логическую переменную 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()
...