I tried these in a playground:
/**
Returns the endIndex of the shorter of the two given collections, and the corresponding index for the longer.
- Parameter first: The first collection to be measured.
- Parameter second: The second collection to be measured.
- Returns: `(first.index(first.startIndex, offsetBy: x), second.index(second.startIndex, offsetBy: x))`, where *x* is defined as `min(first.count, second.count)`.
- Complexity: O(1) since both `first` and `second` conform to `RandomAccessCollection`.
*/
fileprivate func lastCorrespondingIndexes<T: RandomAccessCollection, U: RandomAccessCollection>(_ first: T, _ second: U) -> (T.Index, U.Index) {
print("Dispatch to RandomAccessCollection lastCorrespondingIndexes")
let shortCount = min(first.count, second.count)
return (first.index(first.startIndex, offsetBy: shortCount), second.index(second.startIndex, offsetBy: shortCount))
}
/**
Returns the endIndex of the shorter of the two given collections, and the corresponding index for the longer.
- Parameter first: The first collection to be measured.
- Parameter second: The second collection to be measured.
- Returns: `(first.index(first.startIndex, offsetBy: x), second.index(second.startIndex, offsetBy: x))`, where *x* is defined as `min(first.count, second.count)`.
- Complexity: O(*m*) where *m* is the length of the shorter collection.
*/
fileprivate func lastCorrespondingIndexes<T: Collection, U: Collection>(_ first: T, _ second: U) -> (T.Index, U.Index) {
print("Dispatch to general Collection lastCorrespondingIndexes")
guard let lastCommonNonEndIndexes = Array(zip(first.indices, second.indices).suffix(1)).last else {
// At least one collection was empty.
return (first.index(first.startIndex, offsetBy: +1, limitedBy: first.endIndex) ?? first.endIndex, second.index(second.startIndex, offsetBy: +1, limitedBy: second.endIndex) ?? second.endIndex)
}
// At least one of the returned elements will be its collection's endIndex.
return (first.index(after: lastCommonNonEndIndexes.0), second.index(after: lastCommonNonEndIndexes.1))
}
And put when I tried dispatching them within a struct initializer:
/// A collection of ordered pairs from partially-optional elements from two given sub-collections.
public struct ZipLongest2CollectionToo<FirstBase: Collection, SecondBase: Collection> {
/// Indicator of which collection is longer, and where the uncorresponding suffix of the longer collection begins.
enum SuffixMarker {
/// The first collection is longer.
case longerFirst(FirstBase.Index)
/// The second collection is longer.
case longerSecond(SecondBase.Index)
/// The collections are equal in length.
case equalLength
}
/// The first sub-collection.
let base1: FirstBase
/// The second sub-collection.
let base2: SecondBase
/// Where the uncorresponding suffix of the longer collection begins, or double `nil` if the collections are the same length.
let suffixMarker: SuffixMarker
/**
Creates a collection vending pairs of elments from two given sub-collections.
The elements are `Optional`s of each collections' `Element` type, so when we access beyond the length of the shorter sub-collection, `nil` is returned in that place instead of choking.
- Parameter first: The collection that is the source of the first part of returned elements.
- Parameter second: The collection that is the source of the second part of returned elements.
*/
public init(_ first: FirstBase, _ second: SecondBase) {
base1 = first
base2 = second
let (firstCommonEnd, secondCommonEnd) = lastCorrespondingIndexes(base1, base2)
switch (firstCommonEnd == base1.endIndex, secondCommonEnd == base2.endIndex) {
case (true, false):
suffixMarker = .longerSecond(secondCommonEnd)
case (false, true):
suffixMarker = .longerFirst(firstCommonEnd)
case (true, true):
suffixMarker = .equalLength
default:
preconditionFailure("Can't be reached, since it means that lastCorrespondingIndexes(_: _:) didn't finish")
}
}
}
The general version of lastCorrespondingIndexes is always called, even when the collections both conform to RandomAccessCollection. Is this because the struct specifies only Collection? Or is there something else I'm missing?