Hi all,

This is a crack at a proposal to revise the API of the collection partition 
method, called out as an open issue in the standard library. What's below is a 
much shorter revision of a prior proposal, focused only on the partition 
method. I welcome any feedback you might have!

Thanks,
Nate


––––
This proposal revises the signature for the collection partition algorithm. 
Partitioning is a foundational API for sorting and for searching through sorted 
collections.

Swift-evolution thread: Feedback from standard library team 
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160502/016729.html>
Swift Bug: SR-1965 <https://bugs.swift.org/browse/SR-1965>
Motivation
Based on feedback during the review of proposal SE-0074, Implementation of 
Binary Search Functions 
<https://github.com/apple/swift-evolution/blob/master/proposals/0074-binary-search.md>
 and the list of open issues affecting standard library API stability 
<https://gist.github.com/gribozavr/37e811f12b27c6365fc88e6f9645634d>, this is a 
revised proposal focused only on the existing collection partition method.

The standard library's current partition methods, which partition a mutable 
collection using a binary predicate based on the value of the first element of 
a collection, are used by the standard library's sorting algorithm but don't 
offer more general partitioning functionality. A more general partition 
algorithm using a unary (single-argument) predicate would be more flexible and 
generally useful.

Proposed solution
The standard library should replace the existing partition methods with a 
single method taking a unary predicate. This new method, partition(where:), is 
a mutating method that accepts a unary predicate. The elements of the 
collection are rearranged according to the predicate, so that there is a pivot 
index p where no element before p satisfies the predicate and every element at 
and after p does satisfy the predicate. 

var n = [30, 40, 20, 30, 30, 60, 10]
let p = n.partition(where: { $0 > 30 })
// n == [30, 10, 20, 30, 30, 60, 40]
// p == 5
After partitioning, the predicate returns false for every element in 
n.prefix(upTo: p)and true for every element in n.suffix(from: p).

Detailed design
partition(where:) should be added as a MutableCollection requirement with 
default implementations for mutable and bidirectional mutable collections. Any 
mutable collection can be partitioned, but the bidirectional algorithm 
generally performs far fewer copies. The other two methods can be provided in 
an extension of the Collection protocol.

The proposed APIs are collected here:

protocol MutableCollection {
    // existing requirements
    
    /// Reorders the elements of the collection such that all the
    /// elements that match the predicate are ordered after all the
    /// elements that do not match the predicate.
    ///
    /// - Returns: The index of the first element in the reordered
    ///   collection that matches the predicate.
    /// - Complexity: O(n)
    @discardableResult
    mutating func partition(
        where predicate: @noescape (Iterator.Element) throws-> Bool
    ) rethrows -> Index
}
    
extension MutableCollection {
    @discardableResult
    mutating func partition(
        where predicate: @noescape (Iterator.Element) throws-> Bool
    ) rethrows -> Index
}

extension MutableCollection where Self: BidirectionalCollection {
    @discardableResult
    mutating func partition(
        where predicate: @noescape (Iterator.Element) throws-> Bool
    ) rethrows -> Index
}
A full implementation of the two default implementations can be found in this 
gist <https://gist.github.com/natecook1000/70f36608ecd6236552ce0e9f79b98cff>.

Impact on existing code
The current sorting algorithms would need to be modified to use the new 
partition(where:) method. Other uses of the existing partition methods could be 
flagged or in theory could be replaced programmatically. The replacement code, 
on a mutable collection c:

// old
c.partition()

// new
if let first = c.first {
    c.partition(where: { $0 >= first })
}
A thorough, though not exhaustive, search of GitHub for the existing partition 
method found no real evidence of its use. The evident uses of a partition 
method were mainly either tests from the Swift project or third-party 
implementations similar to the one proposed.

Alternatives considered
To more closely match the existing API, the partition(where:) method could be 
added only as a default implementation for mutable bidirectional collections. 
This would unnecessarily limit access to the algorithm for mutable forward 
collections.
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to