Sequences of distances?

If we have a sequence of Strideable values, should we provide a way to get all the distances between them?

extension Sequence where Element: Strideable {
    /// Returns a sequence consisting of the distances between each adjacent
    /// pair of elements of this sequence.
    ///
    /// - Returns: A lazily-generated sequence of the distances between each
    ///   element of this sequence.  It will be one element shorter than this
    ///   one, making it empty when this sequence has less than two elements.
    @inlinable
    public func distances() -> LazyDistanceSequence<Element.Stride, Self> {
        return LazyDistanceSequence(from: self, measuringBy: {
            $0.distance(to: $1)
        })
    }
}

And we could provide the inverse, provide a list of values given a starting one and a sequence of distances.

extension Sequence {
    /// Returns a sequence consisting of the given initial value followed by
    /// advancing it by the first element of this sequence, then that result
    /// advanced by the second element of this sequence, *etc.*
    ///
    /// - Parameter start: The first element of the returned sequence.
    /// - Parameter advance: A closure that combines a value of the returned
    ///   element type with an element of this sequence's type to give another
    ///   value of the returned element type.
    /// - Returns: A lazily-generated sequence of the running advancement of
    ///   `start` and each element of the sequence.  Since `start` is included,
    ///   the returned sequence will be one element longer than this one.
    @inlinable
    public func progressiveAdvancements<S: Strideable>(from start: S) -> LazyProgressivelyAdvancingSequence<S, Self> where S.Stride == Element {
        return LazyProgressivelyAdvancingSequence(from: start, eachBy: self, advancingBy: {
            $0.advanced(by: $1)
        })
    }
}

Looking at the returns, you see I generalized the idea with lazy and eager versions with custom advancing or measuring closures.

extension Sequence {

    /// Returns the sequence consisting of the given initial value followed by
    /// its combination with the first element of this sequence using the given
    /// closure, then that result combined with the second element, *etc*,
    /// copied into an instance of the given collection type.
    ///
    /// - Parameter start: The first element of the returned sequence.
    /// - Parameter type: A meta-type specifier for the returned collection
    ///   type.
    /// - Parameter advance: A closure that combines a value of the returned
    ///   element type with an element of this sequence's type to give another
    ///   value of the returned element type.
    /// - Returns: A collection of the running combination of `start` and each
    ///   element of the sequence.  Since `start` is included, the returned
    ///   collection will have a length of one greater than the length of the
    ///   sequence.
    ///
    /// - Complexity: O(*n*), where *n* is the length of the sequence.
    public func progressiveAdvancements<T, U: RangeReplaceableCollection>(from start: T, containedAs type: U.Type, advancingBy advance: (T, Element) throws -> T) rethrows -> U where U.Element == T {
        var result = U(CollectionOfOne(start))
        result.reserveCapacity(1 + underestimatedCount)

        var iterator = makeIterator(), element = start
        while let distance = iterator.next() {
            element = try advance(element, distance)
            result.append(element)
        }
        return result
    }

    /// Returns the sequence consisting of the distances between each adjacent
    /// pair of elments, using the given closure to measure the distances, into
    /// a collection of the given type.
    ///
    /// - Parameter type: A meta-type specifier for the returned collection
    ///   type.
    /// - Parameter measure: A closure that takes two values of the element type
    ///   to return the distance from the first argument to the second.
    /// - Returns: A collection of the span between each element, which implies
    ///   a length of one less than the length of the sequence.  (The returned
    ///   collection is empty when the sequence has less than two elements.)
    ///
    /// - Complexity: O(*n*), where *n* is the length of the sequence.
    public func distances<T: RangeReplaceableCollection>(containedAs type: T.Type, measuringBy measure: (Element, Element) throws -> T.Element) rethrows -> T {
        // Can't make a delta sequence from an empty source.
        var iterator = makeIterator()
        guard var previous = iterator.next() else { return T() }

        // Copy each measurement.
        var result = T()
        result.reserveCapacity(underestimatedCount)
        while let current = iterator.next() {
            defer { previous = current }
            try result.append(measure(previous, current))
        }
        return result
    }

}

I guess it's like the running reduce idea from earlier this year, but more palatable since it's oriented around Strideable.

1 Like

While this is a useful function, I'm not sure if it's useful and general-purpose enough to merit inclusion in the standard library.

I'm happy to be proven wrong on that - if there are lots of people who would use this, it may indeed make sense to include.

2 Likes