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?