Для чего используется протокол Sequence (аналог Iterable) в Swift?

Ответ

Протокол Sequence предоставляет доступ к последовательному, возможно одноразовому, потоку значений. Это основа для итерации в Swift.

Ключевые области применения:

  1. Цикл for-in. Любой тип, conforming к Sequence, можно использовать в цикле.
    for number in 1...5 { // Диапазон — это Sequence
        print(number)
    }
  2. Функции высшего порядка и методы Sequence. Стандартные операции для обработки данных.
    let doubled = (1...3).map { $0 * 2 } // [2, 4, 6]
    let evens = (1...10).filter { $0 % 2 == 0 } // [2, 4, 6, 8, 10]
    let sum = (1...5).reduce(0, +) // 15
  3. Создание коллекций. Последовательность можно преобразовать в Array, Set, Dictionary.
    let setFromSequence = Set(1...5) // [5, 2, 3, 1, 4]
  4. Ленивые вычисления. Цепочка преобразований над lazy последовательностью применяется только по мере необходимости.
    let result = (1...1000).lazy
        .filter { $0 % 2 == 0 }
        .map { $0 * $0 }
        .first // Вычисляются только необходимые элементы
  5. Реализация пользовательских итераторов. Вы определяете логику перебора элементов.
    struct Countdown: Sequence {
        let start: Int
        func makeIterator() -> some IteratorProtocol {
            var count = start
            return AnyIterator { // Возвращает следующее значение или nil
                guard count > 0 else { return nil }
                defer { count -= 1 }
                return count
            }
        }
    }
    for num in Countdown(start: 3) { // 3, 2, 1
        print(num)
    }

    Важно: Sequence не гарантирует, что итерация по нему не будет иметь побочных эффектов или что ее можно повторить. Для многократного, безопасного доступа по индексу используется Collection.

Ответ 18+ 🔞

Слушай, ну вот объясняю тебе про этот Sequence, как для дебила, но который хочет понять. Представь себе, блядь, конвейер с пирожками. Не важно, из какой он печки — главное, что пирожки по нему едут один за другим, и ты можешь их снимать. Вот Sequence — это и есть такой конвейер, ёпта. Можешь пройтись по нему разок и всё, а потом он, сука, может сломаться, или пирожки кончатся. Одноразовый, возможно, поток значений, вот и вся суть.

Где эту хуйню применяют:

  1. Цикл for-in. Это ж основа основ! Если твой тип подписался на этот протокол, его можно тыкать в цикл, и он будет выдавать значения, пока не высрется.

    for number in 1...5 { // Ага, диапазон — это тоже Sequence, ёпта
        print(number)
    }
  2. Всякие умные штуки — map, filter, reduce. Ну, те самые функции, где ты пишешь замыкание и чувствуешь себя богом функциональщины. Работают они именно с последовательностями.

    let doubled = (1...3).map { $0 * 2 } // Берем, множим на два — [2, 4, 6]
    let evens = (1...10).filter { $0 % 2 == 0 } // Отфильтровали чётные — [2, 4, 6, 8, 10]
    let sum = (1...5).reduce(0, +) // Сумма всех — 15, ебать!
  3. Создание коллекций из воздуха. Захотел из своей последовательности Array или Set — пожалуйста, на, получай.

    let setFromSequence = Set(1...5) // Получишь сет [5, 2, 3, 1, 4] (порядка, блядь, может и не быть)
  4. Ленивые вычисления — lazy. Вот это, сука, мощная тема. Цепляешь к последовательности lazy, и все дальнейшие map и filter не будут выполняться сразу для всех элементов, а только для тех, до которых ты реально добрался. Экономия памяти — овердохуища!

    let result = (1...1000).lazy
        .filter { $0 % 2 == 0 }
        .map { $0 * $0 }
        .first // Вычислятся только первые элементы, пока не найдут первый чётный квадрат. Остальные — похуй!
  5. Сам себе режиссёр — кастомные итераторы. Хочешь, чтобы значения шли не просто так, а, например, обратным отсчётом? Реализуй свой итератор!

    struct Countdown: Sequence {
        let start: Int
        func makeIterator() -> some IteratorProtocol {
            var count = start
            return AnyIterator { // Эта штука будет возвращать следующее значение или nil
                guard count > 0 else { return nil }
                defer { count -= 1 }
                return count
            }
        }
    }
    for num in Countdown(start: 3) { // Напечатает 3, 2, 1
        print(num)
    }

Важный момент, блядь, запомни: Sequence — это не гарантия. Пройтись по нему можно, но он может быть одноразовым, как презерватив. Итерация может иметь побочные эффекты (например, читать из сети), и повторно запустить её — не факт, что получится. Если тебе нужен надёжный склад, где ты можешь ткнуть пальцем в любой элемент хоть сто раз — это уже Collection. А Sequence — это просто поток, конвейер, из которого shit happens.