По какому правилу sequence(first:next:) решает, что пора завершить обход?
sequence(first:next:) сначала выдаёт исходный элемент, затем после каждого выданного элемента вызывает функцию next. Обход завершается, когда эта функция возвращает nil; само значение nil элементом последовательности не становится.
Подход с sequence(first:next:) появился как компактный способ описывать последовательности, элементы которых вычисляются последовательно, без предварительного построения массива. Он решает задачу ленивой генерации: хранится только текущее состояние, а следующий элемент создаётся по запросу.
Важно отличать условие остановки от значения элемента. Если nil означает завершение, такой генератор не может напрямую выдавать nil как обычный элемент без дополнительного уровня представления, например специального контейнера.
Неверная функция next может сделать обход бесконечным: если она никогда не возвращает nil, последовательность не завершится сама. Кроме того, при ленивом обходе вычисления выполняются только до фактически запрошенного элемента.
Первый элемент передаётся параметром first и выдаётся без предварительного вызова next. При следующем обращении итератор передаёт текущий элемент в функцию next.
Если функция возвращает значение, оно становится следующим элементом и новым текущим состоянием. Если возвращён nil, итератор возвращает nil, после чего обход считается завершённым.
Будут выданы значения 1, 2, 3, 4. После обработки 4 функция возвращает nil, поэтому 5 не появляется. Замыкание вызывается только тогда, когда потребителю нужен следующий элемент; досрочное прекращение потребления предотвращает последующие вычисления.
Функция next не обязана изменять число по простому правилу: она может обращаться к внешнему состоянию или вычислять следующий элемент иным способом. Однако она должна корректно обеспечивать завершение, иначе потребитель, ожидающий конечную последовательность, может зациклиться.
Каждый новый итератор начинает генерацию с first, но последовательность не следует считать контейнером с заранее сохранёнными элементами. Повторный обход обычно заново выполняет вычисления, поэтому побочные эффекты внутри генератора могут повториться.
Сервису нужно постранично получать данные из источника, пока тот возвращает очередную страницу. Возможны два варианта: заранее загрузить все страницы в массив или представить получение следующей страницы через sequence(first:next:).
Массив проще повторно обходить и заранее предоставляет все данные, но требует больше памяти и задерживает начало обработки. Ленивый генератор начинает работу немедленно и загружает только необходимые страницы, однако повторный обход может повторно обратиться к источнику, а ошибка сети не может быть передана обычным next, поскольку его результат использует nil только как сигнал завершения.
Для конечного синхронного источника с небольшими страницами выбран ленивый генератор: обработка начинается сразу, а лишние страницы не загружаются. Если нужны ошибки или гарантированное управление ресурсами, лучше использовать отдельный API с явным результатом ошибки либо специализированную AsyncSequence.
first в результат?Да, first является первым элементом последовательности. Функция next начинает вызываться только после его выдачи, поэтому она не отвечает за вычисление первого значения.
next сразу вернёт nil?Последовательность всё равно выдаст first, а затем завершится. Это позволяет описывать последовательность из ровно одного элемента.
nil как обычный элемент?Если тип элемента сам является optional, механизм становится неоднозначным на уровне результата next: возвращённый nil трактуется как конец последовательности. Чтобы различать «элемент со значением nil» и завершение, нужно обернуть optional в дополнительный тип, например в структуру или другой контейнер.