> On Apr 17, 2016, at 8:46 AM, plx via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
> I like the idea. It worries me a bit if the general recipe for such 
> situations is to add dedicated-purpose methods like 
> `_customIndexOfMinComparableElement` (etc.) to standard-library protocols; it 
> *will* work, but is only going to work for the methods that get such 
> treatment.

Agreed! I'm not sure if that's part of the generics manifesto or not, but it'd 
be great to have conditionally applied dynamically dispatched methods for cases 
like this.

> If it were feasible, I’d *greatly* prefer having some more-general way to 
> (conditionally?) add overridable methods to a protocol, e.g.:
> 
>   // made-up declaration, all declared methods below are now overridable
>   // on suitable types
>   overridable extension Collection where Iterator.Element: Comparable {
>     
>     func min() -> Iterator.Element?    
> 
>   }
> 
> …but am not sure that’s even realistically possible.
> 
> The reason I bring it up is that I’d hope that `indexOf`, `contains`, and 
> also `upperBound`/`lowerBound` would merit a similar treatment to that 
> proposed here for min, max, and minmax (if not already given such; if they 
> get added to standard-library; etc.).

The customization points in the proposal are modeled after the two existing 
ones in the standard library for `contains` and `index(of:)`. You can see them 
here:

https://github.com/apple/swift/blob/master/stdlib/public/core/Sequence.swift#L190
https://github.com/apple/swift/blob/master/stdlib/public/core/Collection.swift#L184

> Moving on, wrt these min/max elements themselves: I think any expectation 
> about *which* index gets returned should be document―either no such 
> expectation in general, or a specific expectation, or that each concrete 
> collection should document the expectation, etc.―because for the 
> minIndex/maxIndex methods different approaches can yield different results.
> 
> EG: consider an order-maintaining collection that has contents ~ 
> `[1,1,1,2,2,2,3,3,3]`. There are multiple candidates for both `minIndex` and 
> `maxIndex`.
> 
> I don’t have a strong opinion on what the right preference would be here, but 
> I think whatever the behavior winds up being should be at least documented.
> 
> Finally, FWIW if more of these semi-hidden methods become something exposed 
> to users (as “advanced” options, perhaps, but still) I think replacing `??` 
> with something like
> 
>   enum AdvancedCustomizationPointResult<T> {
>     case NotCustomized // like `nil` for ??
>     case NoResult // like `Optional(nil)` for ??
>     case Result(T) // like `Optional(T)` for ??
>   }
> 
> …might be worth considering (except with a better name than that). I’m not 
> trying to backdoor optional protocol methods or anything, it just seems 
> advisable to more-explicitly represent the intent (?? certainly works but 
> feels a bit obscure).
> 
>> On Apr 17, 2016, at 1:44 AM, Nate Cook via swift-evolution 
>> <swift-evolution@swift.org> wrote:
>> 
>> Hello all,
>> 
>> Attached is a draft of a proposal to expand the min and max sequence APIs to 
>> better handle collections and to support future sorted 
>> sequences/collections. The proposal is in a gist here and inlined 
>> below―would love to hear any comments or feedback before submitting the 
>> proposal.
>> 
>> Nate
>> 
>> 
>> Proposal: Expanded min/max algorithms
>> This proposal would expand on the min() and max() sequence methods to add 
>> methods that return the corresponding index for a collection, efficiently 
>> find the minimum and maximum elements or indices at the same time, and 
>> provide extension points for sorted collections to provide all these results 
>> more efficiently.
>> 
>> Related Bugs: SR-889 and SR-890
>> 
>> Motivation
>> The Sequence protocol currently offers min() and max() methods that return 
>> the minimum and maximum elements of a sequence or collection. Unfortunately, 
>> there are applications where these methods do not provide enough flexibility 
>> to be useful.
>> 
>> First, if the user of a collection wants not just to get the minimum value 
>> but also to operate on it in some way (e.g., mutation or just accessing it 
>> multiple times), she would need the index of the minimum element. The 
>> current APIs don't support that, so she would need to write her own.
>> 
>> Second, the writer of a sorted collection is currently unable to provide 
>> efficient responses to the min() and max() methods when used in a generic 
>> context, even though these should be O(1) operations. Just like Set can 
>> respond quickly to contains(_:) even in a generic context, so too should new 
>> sorted collections be able to optimize their responses.
>> 
>> Finally, getting the minimum and maximum elements (or indices) of a 
>> collection or sequence currently requires calling both min() and max(). With 
>> two calls, every element is iterated and compared twice. When you need both 
>> results, finding both the minimum and the maximum at the same time is more 
>> efficient, requiring only a single pass and 25% fewer comparisons.
>> 
>> Proposed solution
>> This proposal has three parts:
>> 
>> Adding minIndex() and maxIndex() methods to Collection that return the index 
>> of the minimum and maximum elements, respectively.
>> 
>> let numbers = [30, 40, 10, 20, 60, 50]
>> 
>> if let i = numbers.minIndex() {
>>     print("\(i): \(numbers[i])")               // 2: 10
>> }
>> Adding minmax() and minmaxIndices() methods to Sequence and Collection, 
>> respectively, to calculate the values (or indices) of the minimum and 
>> maximum elements simultaneously.
>> 
>> if let result = numbers.minmax() {
>>     // result == (minimum: 10, maximum: 60)
>>     // ...
>> }
>> if let i = numbers.minmaxIndices() {
>>     // i == (minimum: 2, maximum: 4)
>>     print("\(i.minimum): \(numbers[i.minimum])")
>> }
>> Adding customization points for sequences and collections that can offer 
>> more efficient results: 
>> _customMinComparableElement()/_customMaxComparableElement() for Sequence and 
>> _customIndexOfMinComparableElement()/_customIndexOfMaxComparableElement()for 
>> Collection.
>> 
>> Detailed design
>> The following methods would be added to the visible public APIs of Sequence 
>> and Collection as default implementations.
>> 
>> extension Sequence {
>>     /// Returns the minimum and maximum values of `self`, using 
>>     /// `isOrderedBefore` to compare elements, or `nil` if the sequence
>>     /// has no elements.
>>     func minmax(@noescape isOrderedBefore isOrderedBefore: 
>>         (Iterator.Element, Iterator.Element) throws -> Bool
>>         ) rethrows -> (min: Iterator.Element, max: Iterator.Element)?
>> }
>> 
>> extension Sequence where Iterator.Element: Comparable {
>>     /// Returns the minimum and maximum values of `self`, or `nil` 
>>     /// if the sequence has no elements.
>>     func minmax() -> (min: Iterator.Element, max: Iterator.Element)?
>> }
>> 
>> extension Collection {
>>     /// Returns the index of the minimum element of `self`, using 
>>     /// `isOrderedBefore` to compare elements, or `nil` if the
>>     /// collection has no elements.
>>     func minIndex(@noescape isOrderedBefore isOrderedBefore: 
>>         (Iterator.Element, Iterator.Element) throws -> Bool
>>         ) rethrows -> Index?
>>         
>>     /// Returns the index of the maximum element of `self`, using 
>>     /// `isOrderedBefore` to compare elements, or `nil` if the
>>     /// collection has no elements.
>>     func maxIndex(@noescape isOrderedBefore isOrderedBefore: 
>>         (Iterator.Element, Iterator.Element) throws -> Bool
>>         ) rethrows -> Index?
>> 
>>     /// Returns the indices of the minimum and maximum elements of `self`, 
>>     /// using `isOrderedBefore` to compare elements, or `nil` if the
>>     /// collection has no elements.
>>     func minmaxIndices(@noescape isOrderedBefore isOrderedBefore: 
>>         (Iterator.Element, Iterator.Element) throws -> Bool
>>         ) rethrows -> (minIndex: Index, maxIndex: Index)?
>> }
>> 
>> extension Collection where Iterator.Element: Comparable {
>>     /// Returns the index of the minimum element of `self`, or `nil` 
>>     /// if the collection has no elements.
>>     func minIndex() -> Index?
>>         
>>     /// Returns the index of the maximum element of `self`, or `nil` 
>>     /// if the collection has no elements.
>>     func maxIndex() -> Index?
>> 
>>     /// Returns the indices of the minimum and maximum elements of `self`, 
>>     /// or `nil` if the collection has no elements.
>>     func minmaxIndices() -> (minIndex: Index, maxIndex: Index)?
>> }
>> The customization points would be added to Sequence and Collection as 
>> protocol requirements, along with default implementations that return nil. 
>> The existing min()and max() methods would be updated to call the 
>> corresponding methods before iterating over the entire sequence.
>> 
>> protocol Sequence {
>>     // ...
>>     
>>     /// Returns the minimum element as `Optional(element)` or `Optional(nil)`
>>     /// if the sequence has no elements. The uncustomized version returns
>>     /// `nil`.
>>     func _customMinComparableElement() -> Iterator.Element??
>>     
>>     /// Returns the maximum element as `Optional(element)` or `Optional(nil)`
>>     /// if the sequence has no elements. The uncustomized version returns
>>     /// `nil`.
>>     func _customMaxComparableElement() -> Iterator.Element??
>> }
>> 
>> protocol Collection {
>>     // ...
>>     
>>     /// Returns the index of the minimum element as `Optional(index)` or
>>     /// `Optional(nil)` if the sequence has no elements. The uncustomized 
>>     /// version returns `nil`.
>>     func _customIndexOfMinComparableElement() -> Index??
>>     
>>     /// Returns the index of the maximum element as `Optional(index)` or
>>     /// `Optional(nil)` if the sequence has no elements. The uncustomized 
>>     /// version returns `nil`.
>>     func _customIndexOfMaxComparableElement() -> Index??
>> }
>> Minmax Algorithm
>> 
>> The minmax() algorithm finds the minimum and maximum elements of a sequence 
>> in one pass more efficiently than consecutive calls to min() and max(). This 
>> optimization comes from iterating over a sequence two elements at a time. 
>> 
>> In each iteration, two consecutive elements are compared with each other. 
>> Only the lesser element could be a minimum for the whole sequence, so it is 
>> compared with the current minimum, while only the greater element could be a 
>> maximum, so it is compared with the current maximum. This works out to 3 
>> comparisons for every 2 elements vs. 2 comparisons for every element when 
>> the minimum and maximum are found individually.
>> 
>> Impact on existing code
>> As new APIs these should have no effect on existing code.
>> 
>> Alternatives considered
>> None.
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to