1

I want to implement some binary search like functions that operate on collections of sorted elements that are randomly accessible (in constant time), and where index arithmetic is supported (for deriving a mid point). I could add these methods as an extension or Array. However, I'd prefer to add them to a more generic protocol that supports the necessary functionality so that my extension can be used more widely.

Which protocol should I use, and how do I go about finding such protocols in future? I did a search for "Swift collection protocol hierarchy" and similar, and I've not found anything useful beyond various red herrings.

Benjohn
  • 13,228
  • 9
  • 65
  • 127
  • You *can* implement it for arbitrary collections (example: https://stackoverflow.com/a/40226976/1187415). A [RandomAccessCollection](https://developer.apple.com/reference/swift/randomaccesscollection) guarantees that you can move the index in O(1) time. – Martin R May 31 '17 at 17:52
  • This is a good place to start: https://www.skilled.io/u/playgroundscon/sequence-and-collection-swift – Lou Franco May 31 '17 at 18:04

1 Answers1

1

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.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382