Thank you for your comments.
I think additional index manipulation and collection scanning API is needed, and your proposal cover an important part of it.
I also have some clarifications and comments inline:
Thanks Hooman! Those do look like useful extensions. I think the proposal should stay focused on the APIs it includes already.
public func offset(of index: Index) -> IndexDistance
public func index(atOffset offset: IndexDistance) -> Index
I like these, but I doubt they would get much traction, since they're essentially substituting startIndex into existing methods. I have thought it would be nice to have startIndex as the default parameter to those methods, though, so you could write either of these:
let i = c.index(c.startIndex, offsetBy: 5) // current
let i = c.index(offsetBy: 5) // nice addition
That is right. The main function is `offset` that I use a lot and for the reverse, your suggestion seems better. I am using `offset` quite a bit but I don’t know if it is generally as useful for other people as it is for me.
public func index(of element: Iterator.Element, from firstIndex: Index) -> Index?
public func index(from firstIndex: Index, where predicate: @noescape (Iterator.Element) throws -> Bool) rethrows -> Index?
I have the same reaction to these. Because indices are shared between collections and slices, instead of using a starting index, Swift's collection operations can just work on a slice. So instead of calling
let i = c.index(of: "A", from: firstIndex)
you can call
let i = c.suffix(from: firstIndex).index(of: "A”)
The point is: The `i` above is an index into the (discarded) slice returned by `suffix()`, not the collection `c`. In general, it does not work correctly on the original collection. The behavior of slice indexes have changed a couple of times and is not totally consistent or guaranteed for different concrete collections. That is the reason I am proposing the above function to provide a sure way to have this functionality working properly and I find it extremely useful. Again I don’t know about others.
It seems that the subject of the interaction between slice indexes and the parent collections need further clarifications and specification from the core Swift team.
public func index<C: Collection where ...>(of elementSequence: C) -> Index?
public func index<C: Collection where ...>(of elementSequence: C, from firstIndex: Index) -> Index?
These methods we don't have at all currently, and could really use! I would definitely support a proposal for finding a subsequence of a collection. There are several algorithms beyond the naive approach, so it would be interesting to see if / how a proposal could use those in a generic context.
I updated the gist <https://gist.github.com/hooman/e77cc0e955a1db672ae49e45b0038d04>\. Besides some corrections and removing a couple of extension constraints, I merged the two functions above into:
public func index<C: Collection where ...>(of elementSequence: C, from firstIndex: Index? = nil) -> Index?
I think the basic implementation in the gist is good enough for many cases and we can specialize for array. As long as the collection and the sub-collection are not huge, performance should be fine.
On the other hand, I am too busy to seriously propose and pursue its addition. If enough people find it worthy of general inclusion into the standard library, somebody will pick it up, but not me.
I didn’t intend to hijack your proposal, but I thought some comments would help clarify things.
Thank you again,
Hooman
···
On May 10, 2016, at 4:52 PM, Nate Cook <natecook@gmail.com> wrote:
On May 10, 2016, at 4:18 PM, Hooman Mehr <hooman@mac.com <mailto:hooman@mac.com>> wrote:
Thanks again!
Nate
Look at the comments for the example usage. For `offset` function, see the source code for usage.
Hooman
On May 10, 2016, at 11:54 AM, Nate Cook via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I've needed these in the past and used them in other languages—any feedback on this idea?
Add last(where:) and lastIndex(where:) Methods to Bidirectional Collections
The standard library should include methods for finding the last element of a bidirectional collection that matches a predicate, along with the index of that element.
Motivation
The standard library currently has (or will soon have) methods that perform a linear search from the beginning of a collection to find an element that matches a predicate:
let a = [20, 30, 10, 40, 20, 30, 10, 40, 20]
a.first(where: { $0 > 25 }) // 30
a.index(of: 10) // 2
a.index(where: { $0 > 25 }) // 1
Unfortunately, there is no such method that searches from the end of a bidirectional collection. Finding the last of particular kind of element has multiple applications, particularly with text, such as wrapping a long string into lines of a maximum length or trimming whitespace from the beginning and end of a string.
This limitation can be worked around by using the methods above on the reversed collection, but the resulting code is truly dreadful. For example, to find the corresponding last index to a.index(where: { $0 > 25 }), this unholy incantation is required:
(a.reversed().index(where: { $0 > 25 })?.base).flatMap({ a.index(before: $0) })
Wat.
Proposed solution
Bidirectional collections should include three new methods for symmetry with the existing forward-searching APIs: last(where:), lastIndex(where:), and lastIndex(of:), specifically for collections of Equatable elements.
These additions would remove the need for searching in a reversed collection and allow code like the following:
a.last(where: { $0 > 25 }) // 40
a.lastIndex(of: 10) // 6
a.lastIndex(where: { $0 > 25 }) // 7
Much better!
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution