[Pitch] Adding Strideable Sequences

A Swift Sequence may represent many kinds of sequential elements: natural numbers, word from a source text, hues in a color wheel, bytes in a memory allocation, and so forth. It is natural and common to stride through sequences, especially those that aren't necessarily numerical in nature. A stride allows you to collect or consume spaced-out values at a faster pace. For example, you might want to look at "every 4th byte" to process alpha channels, "every other row" for spreadsheet highlights, or "every two hundredth word" for frequency analysis.

Swift's built-in stride functions are number based. They are useful for striding through integer and floating point numbers. They can act as integer-based collection indices. These functions aren't, however, suitable for skipping through non-numerical, possibly infinite sequences, which may or may not be multipass or otherwise indexable.

  • For example, you might create a color progression Sequence and parameterize how quickly to procede from, for example, red through orange, yellow, green, and beyond.

  • You cannot use indexing features without creating an intermediate array.

This proposal extends Sequence to introduce strides across any base.

gist with proposal details: stridingBy.md · GitHub

3 Likes

so would it just discard all the “filler” values if the sequence is single-pass? this seems pretty unperformant when using it on iterators of a collection that actually supports random access. You would be much better off just indexing into the collection directly.

1 Like

Yeah, it would be worth adding a specialization for RandomAccessCollection. There are lots of sequences and collections that don't support random access that we still might want to stride through, though — we should support them.

Maybe this is relevant to the proposal here:

1 Like

More performant implementations are greatly welcome.

Here's an implementation for RandomAccessStridedSequence we can use:

RandomAccessStridedSequence
public struct RandomAccessStridedSequence<BaseCollection: RandomAccessCollection> : Collection {
    public typealias Stride = Int

    public typealias Index = BaseCollection.Index

    public var startIndex: Index {
        return _collection.startIndex
    }

    public var endIndex: Index {
        return _collection.endIndex
    }

    public func index(after i: Index) -> Index {
        return _collection.index(i, offsetBy: _strideLength, limitedBy: _collection.endIndex) ?? _collection.endIndex
    }

    public subscript(i: Index) -> BaseCollection.Element {
        return _collection[i]
    }

    /// Access only through `Sequence.striding(by:)`
    internal init(_ collection: BaseCollection, stride strideLength: RandomAccessStridedSequence.Stride) {
        _collection = collection
        _strideLength = strideLength
    }

    internal var _collection: BaseCollection
    internal var _strideLength: RandomAccessStridedSequence.Stride
}

extension RandomAccessCollection {
    func striding(by strideLength: RandomAccessStridedSequence<Self>.Stride) -> RandomAccessStridedSequence<Self> {
        return RandomAccessStridedSequence(self, stride: strideLength)
    }
}

The gist is updated with an annotated version of RandomAccessStridedSequence. Thanks @soroush