3

I am trying to convert the following Swift 2.3 code:

//Example usage:
//(0 ..< 778).binarySearch { $0 < 145 } // 145
extension CollectionType where Index: RandomAccessIndexType {

    /// Finds such index N that predicate is true for all elements up to
    /// but not including the index N, and is false for all elements
    /// starting with index N.
    /// Behavior is undefined if there is no such N.
    func binarySearch(predicate: Generator.Element -> Bool)
        -> Index {
        var low = startIndex
        var high = endIndex
        while low != high {
            let mid = low.advancedBy(low.distanceTo(high) / 2)
            if predicate(self[mid]) {
                low = mid.advancedBy(1)
            } else {
                high = mid
            }
        }
        return low
    }
}

into Swift 3 as follows:

//Example usage:
//(0 ..< 778).binarySearch { $0 < 145 } // 145
extension Collection where Index: Strideable {

    /// Finds such index N that predicate is true for all elements up to
    /// but not including the index N, and is false for all elements
    /// starting with index N.
    /// Behavior is undefined if there is no such N.
    func binarySearch(predicate: (Generator.Element) -> Bool)
        -> Index {
        var low = startIndex
        var high = endIndex
        while low != high {
            let mid = low.advanced(by: low.distance(to: high) / 2)
            if predicate(self[mid]) {
                low = mid.advanced(to: 1)
            } else {
                high = mid
            }
        }
        return low
    }
}

The error

Binary operator '/' cannot be applied to operands of type 'self.Index.Stride' and 'Int'

is thrown at let mid = low.advanced(by: low.distance(to: high) / 2)

Any help on how to fix it?

eshirima
  • 3,837
  • 5
  • 37
  • 61
Navi
  • 45
  • 7
  • Is your Swift 2 code from http://stackoverflow.com/a/33674192/1187415? You should add a link to the original if quoting code from other sources. – Martin R Oct 24 '16 at 20:36
  • Yes, thanks for finding the link. I couldn't track that down. – Navi Oct 24 '16 at 20:50

1 Answers1

7

In Swift 3, "Collections move their index", compare A New Model for Collections and Indices on Swift evolution. In particular, you don't call advancedBy() on an index, but an index() method on the collection to advance indices.

So your method would be implemented in Swift 3 as

extension RandomAccessCollection {

    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
    }
}

The same method would also compile (and work) as an extension of the more general Collection type, but – as Vadim Yelagin correctly remarked, is very inefficient if the index/offset calculations cannot be done in constant time.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • It still makes sense to define this only on RandomAccessCollection because the algorithm is ineffective unless distance and offset can be computed in constant time. – Vadim Yelagin Aug 23 '18 at 15:48