[swift-evolution] [Discussion] Binary search, lower bound, upper bound, equal range

Jeff Hajewski jeff.hajewski at gmail.com
Mon Feb 15 15:46:21 CST 2016


Dave,

Thanks a lot for the feedback - it is greatly appreciated. With respect to your comment about the unary partitioning predicate, I really like that idea and hadn’t thought of the problem like that. Excellent suggestion!

Thanks
Jeff

> On Feb 15, 2016, at 4:34 PM, Dave Abrahams via swift-evolution <swift-evolution at swift.org> wrote:
> 
> 
> on Mon Feb 15 2016, Jeff Hajewski <swift-evolution at swift.org> wrote:
> 
>> Hello,
>> 
>> I am working on SR-368 <https://bugs.swift.org/browse/SR-368> and am hoping to get some feedback from the community.
>> 
>> Description
>> Implement analogs to C++’s binary_search, lower_bound, upper_bound, and equal_range algorithms.
>> 
>> Initial Proposal
>> I propose adding four methods for generic Array and adding four
>> methods for Arrays that conform to Comparable:
>> 
>> Array
>> @warn_unused_result
>> func lowerBound(value: Array.Element, @noescape isOrderedBefore: (lhs:
>> Array.Element, rhs: Array.Element) -> Bool) -> Int?
>> 
>> @warn_unused_result
>> func upperBound(value: Array.Element, @noescape isOrderedBefore: (lhs:
>> Array.Element, rhs: Array.Element) -> Bool) -> Int?
>> 
>> @warn_unused_result
>> func binarySearch(value: Array.Element, @noescape
>> isOrderedBefore:(lhs: Array.Element, rhs: Array.Element) -> Bool) ->
>> Bool
>> 
>> @warn_unused_result
>> func equalRange(value: Array.Element, @noescape isOrderedBefore: (lhs:
>> Array.Element, rhs: Array.Element) -> Bool) -> Range<Int>?
>> 
>> Array: Comparable
>> @warn_unused_result
>> func lowerBound(value: Array.Element) -> Int?
>> 
>> @warn_unused_result
>> func upperBound(value: Array.Element) -> Int?
>> 
>> @warn_unused_result
>> func binarySearch(value: Array.Element) -> Bool
>> 
>> @warn_unused_result
>> func equalRange(value: Array.Element) -> Range<Int>?
>> 
>> Discussion
>> The main issue I’d like to discuss is how do we want to add these to Swift? My thoughts are as follows:
>> 
>> These should be as generic as possible. I initially started at the
>> CollectionType level but it seemed that the problem was ultimately
>> reduced to an Array (through the use of various protocols, etc.). 
> 
> These should be defined on CollectionType Note that you don't need
> random access to get an advantage from binary search if the comparison
> is sufficiently expensive.
> 
>> For example, suppose we implemented these methods as an extension to
>> CollectionType, if we were to try and call binarySearch on a set, we
>> would first need to sort the set, which would return an array, and
>> then we would call binarySearch on that array. 
> 
> It should work on any arbitrary collection.  The collections you know
> are not the only ones that matter.  For examples, consider binary
> searching the result of applying a lazy map or filter to some sorted
> array.
> 
>> Similarly, it seems at the SequenceType level the problem ultimately
>> reduces to using Array.
> 
> Sequences don't have indices; you can't binary search them.
> 
>> Does it make sense to handle these as public functions? I tend to
>> think not as it seems less idiomatic.  I suggest eight implementations
>> similar to how sort() is handled. If the calling array’s elements
>> conform to Comparable, then no isOrderedBefore closure is required
>> since we can use “<“, otherwise the user should supply a closure to
>> allow the algorithm determine how the array elements are ordered.
> 
> The general form takes a closure that partitions the collection; see
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2001/n1313.html and
> http://wg21.cmeerw.net/lwg/issue270
> 
> I would suggest avoiding the API choice that requires both a comparison
> closure *and* an element against which to compare, as a unary
> partitioning predicate is much simpler.
> 
>> Similar to the C++ implementations, I avoid recursion and favor
>> while-loops.
> 
> 
> Have an implementation is important as proof-of-concept and for
> performance testing, but I don't consider the details important at this
> stage.
> 
>> These methods should be preconditioned on the calling
>> array be partitioned with respect to the passed value. 
>> As far as I’m aware, there is no “isPartitioned(value:)” method
>> currently available. 
>> 
>> Should we add this as well? We could use this functionality and not
>> add a isPartitioned method but if we are adding this functionality is
>> there a good reason not to give the public access?  
> 
> Seems like a separable proposal.
> 
>> The alternative is to not set a precondition and document that the
>> results may not be valid if the calling array is not partitioned with
>> respect to value. 
> 
> That *is* a precondition.  Not all preconditions can or should be
> verfied at runtime.  We would never make the caller pay to actually
> verify at runtime that the collection is partitioned w.r.t. the closure;
> that would make the whole operation O(N) and effectively discard any
> benefit of binary-searching.
> 
>> I favor the preconditioning approach.
>> 
>> Any and all feedback is greatly appreciated!
> 
> 
> HTH,
> Dave
> 
> 
> -- 
> -Dave
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution



More information about the swift-evolution mailing list