 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: {
})
}
}

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