[swift-evolution] Proposal: Add scan, takeWhile, dropWhile, and iterate to the stdlib

Kevin Ballard kevin at sb.org
Thu Jan 21 18:17:30 CST 2016


I'm rather opposed to the idea of taking the methods that take indexes
and overloading them to take predicates instead. They're extremely
different functionality. An index is a value that, once generated, is
constant-time to use, and therefore the functions that take them are
typically O(1). For example, CollectionType.suffixFrom() is documented
explicitly as being O(1).

But functions taking a predicate must be O(N), because they have to
evaluate the predicate on every element in turn until it returns true
(or false, depending on the method in question).

So if we overload suffixFrom() to take a predicate, then we have one
function with one overload that's always O(1), and one overload that's
always O(N), and that is a great way to confuse people and hide
performance issues.

This is also why I'm particularly fond of the takeWhile / dropWhile
terminology. Besides the rather extensive precedent, the inclusion of
the word "while" makes it clear that it's iterating over the
sequence/collection, which means it's intuitively O(N).

-Kevin Ballard

On Thu, Jan 21, 2016, at 02:57 PM, David Smith wrote:
> To be honest these all seem equally "weird" looking to me, so my hope
> is that it's just a matter of familiarity 😊 My personal inclination is
> still that overloading suffixFrom with a predicate-taking variant is
> the way to go. That way it occupies the same mental slot as the
> existing suffixFrom method, and all you have to decide when using it
> is "do I want to specify where to suffix from, or how to find where to
> suffix from?". e.g. it basically becomes sugar that turns
>
> let idx = foo.indexOf { … } let suffix = foo.suffixFrom(idx)
>
> into
>
> let suffix = foo.suffixFrom { … }
>
> with the bonus feature that it works on single-pass sequences.
>
> David
>
>> On Jan 13, 2016, at 11:52 PM, Kevin Ballard via swift-evolution <swift-
>> evolution at swift.org> wrote:
>>
>> "suffixAfter" sounds like the equivalent of dropFirst(_:), i.e. it
>> sounds like it should take a count of elements to skip. Similarly,
>> actually trying an expression that takes a predicate looks weird:
>>
>> seq.suffixAfter({ isspace($0) })
>>
>> Even knowing what it's supposed to do, it's hard for me to read that
>> expression in any sensible fashion.
>>
>> Also, I'm not sure the "noun phrase" convention really makes sense
>> for SequenceType methods. They're not technically mutating methods,
>> but single-pass collections are in fact destructively mutated by Array-
>> returning sequence methods (and the methods that return SubSequence
>> also destructively mutate upon any access to the returned
>> subsequence). Which is to say, despite not being marked as
>> "mutating", they do in fact behave like mutating methods for single-
>> pass sequences. Which suggests that verb phrases are perfectly fine.
>>
>> -Kevin Ballard
>>
>> On Wed, Jan 13, 2016, at 08:36 PM, David Smith via swift-
>> evolution wrote:
>>>
>>> Rob Rix pointed out that "suffixAfter" would meet all my original
>>> criteria. Not sure if keeping the original "match the stuff to drop
>>> rather than the stuff to keep" semantics are critical, but this
>>> gives us an option for either way 😊
>>>
>>> David
>>>
>>> On Jan 13, 2016, at 6:40 PM, David Smith via swift-evolution <swift-
>>> evolution at swift.org> wrote:
>>>> Naming conventions would suggest that something returning a new
>>>> collection should be named with a noun phrase describing what it
>>>> returns. Unfortunately, most of the ones I can think of off the top
>>>> of my head are fairly clunky. "suffixFromFirstNonMatchingElement"
>>>> describes what it does, but I haven't thought of a non-painful way
>>>> to say that yet. "suffixExcluding" is almost right, but it
>>>> incorrectly implies (to my eye at least) that the returned
>>>> collection excludes all elements matching the predicate, rather
>>>> than just matching prefixes. Hm, what about flipping the predicate
>>>> and getting a "suffixFrom" overload that takes a predicate for the
>>>> first matching element to be included, rather than the last
>>>> matching element to be excluded?
>>>>
>>>> David
>>>>
>>>>> On Jan 13, 2016, at 5:54 PM, Dany St-Amant via swift-evolution <swift-
>>>>> evolution at swift.org> wrote:
>>>>>
>>>>> The dropWhile sounds weird to me, I usually would see such
>>>>> functionality as a dropUntil; I discard stuff until I see what I
>>>>> want. Your example below doesn’t use dropWhile, but skipWhile;
>>>>> which sounds a bit better that dropWhile as one skip what he
>>>>> doesn’t want.
>>>>>
>>>>> What do the other languages use? A dropWhile, skipWhile or
>>>>> dropUntil concept?
>>>>>
>>>>> Dany
>>>>>
>>>>>
>>>>>> Le 11 janv. 2016 à 01:20, Kevin Ballard via swift-evolution <swift-
>>>>>> evolution at swift.org> a écrit :
>>>>>>
>>>>>> Here's a few toy examples, if it helps:
>>>>>>
>>>>>> // list of all powers of 2 below some limit iterate(1, apply: {
>>>>>> $0 * 2 }).takeWhile({ $0 < limit })
>>>>>>
>>>>>> // first "word" of a string, skipping whitespace let cs =
>>>>>> NSCharacterSet.whitespaceCharacterSet()
>>>>>> String(str.unicodeScalars.skipWhile({
>>>>>> cs.longCharacterIsMember($0.value) })
>>>>>> .takeWhile({ !cs.longCharacterIsMember($0.value) }))
>>>>>>
>>>>>> // running total of an array of numbers numbers.scan(, combine:
>>>>>> +).dropFirst()
>>>>>>
>>>>>> // infinite fibonacci sequence iterate((,1), apply: { ($1, $0+$1)
>>>>>> }).lazy.map({$1})
>>>>>>
>>>>>> -Kevin Ballard
>>>>>>
>>>>>>>>> On Mon, Dec 28, 2015, at 03:59 PM, Kevin Ballard wrote:
>>>>>>>>> >
>>>>>>>>> > ## Detailed design
>>>>>>>>> >
>>>>>>>>> > We add the following extension to SequenceType:
>>>>>>>>> >
>>>>>>>>> > extension SequenceType {     func scan<T>(initial: T,
>>>>>>>>> > @noescape combine: (T, Self.Generator.Element) throws -> T)
>>>>>>>>> > rethrows -> [T]     func dropWhile(@noescape dropElement:
>>>>>>>>> > (Self.Generator.Element) throws -> Bool) rethrows ->
>>>>>>>>> > [Self.Generator.Element]     func takeWhile(@noescape
>>>>>>>>> > takeElement: (Self.Generator.Element) throws -> Bool)
>>>>>>>>> > rethrows -> [Self.Generator.Element] }
>>>>>>>>> >
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> swift-evolution mailing list swift-evolution at swift.org
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>
>>>> _______________________________________________
>>>> swift-evolution mailing list swift-evolution at swift.org
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>>> _________________________________________________
>>> swift-evolution mailing list swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>>
>> _______________________________________________
>> swift-evolution mailing list swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160121/aac62683/attachment.html>


More information about the swift-evolution mailing list