For the binary search you need to find the "midpoint" between two
index positions, and that is possible for any Collection
, because
its associated IndexDistance
is required to be a SignedInteger
.
A possible implementation (taken from https://stackoverflow.com/a/40226976/1187415) is
extension Collection {
func binarySearch(predicate: (Iterator.Element) -> Bool) -> Index {
var low = startIndex
var high = endIndex
while low != high {
let mid = index(low, offsetBy: distance(from: low, to: high)/2)
if predicate(self[mid]) {
low = index(after: mid)
} else {
high = mid
}
}
return low
}
}
Here distance(from:to:)
returns a IndexDistance
which can be divided by 2 because it
conforms to SignedInteger
(and therefore to IntegerArithmetic
), the result
can be used in index(_:offsetBy:)
.
The documentation of both methods states that the
complexity is
O(1) if the collection conforms to RandomAccessCollection; otherwise, O(n), where n is the absolute value of n.
So you can implement it as an extension of RandomAccessCollection
if you want to guarantee that it is only used on collections for which
moving indices and measuring distances is a O(1) operation.
Or you implement it as an extension of Collection
so that it can
be used universally. It will be "fast" (in the above sense) when
called on a RandomAccessCollection
and might be "slow" otherwise.