[swift-evolution] [Idea] Add an (Index, Element) sequence to CollectionType

Dany St-Amant dsa.mls at icloud.com
Mon Dec 28 20:30:00 CST 2015


The original example contains a bug which is present on all looping version/alternative due to the mutating nature of the array. Using the zip implementation:

var array = [ "", "a", "b", "c", "", "d" ]
var secondHalf = array[array.count/2..<array.count]
for (index, element) in zip(secondHalf.indices, secondHalf) {
    if element == "" {
        secondHalf.removeAtIndex(index)
    }
}

The variable index cycles through 3,4 and 5; but in order to be able to remove the right element beyond the first removal, the variable index should have cycled through 3, 4 and 4 (as some elements got shifted after the first mutation). Mutating the array/list which one loops over is a risky business and is a nice source of bugs (including infinite loop). If this (Index, Element) is further investigated, it should consider that one may also want to do a insert(:atIndex:), and may expect the (Index, Element) to have proper Index but only for the original Element.

Dany St-Amant


> Le 28 déc. 2015 à 01:06, Kevin Ballard via swift-evolution <swift-evolution at swift.org> a écrit :
> 
> What you're asking for can already be done with `zip(col.indices, col)`. And in my experience the need for this sort of thing is rare enough that there's no need to have a dedicated property for it in the stdlib. The few times that I've needed this sort of thing, I've always just said
>  
> for index in col.indices {
>     let elt = col[index]
>     // ...
> }
>  
> and that's pretty simple. But if I ever did need to map it, I'd just use the aforementioned zip() expression.
>  
> -Kevin Ballard
>  
> On Sun, Dec 27, 2015, at 12:08 AM, Patrick Pijnappel via swift-evolution wrote:
>> -- Introduction
>>  
>> There should be a property on CollectionType that returns a sequence of (Index, Element) tuples.
>> Currently enumerate() is often used instead, but it is not well suited to the task and can lead to bugs.
>>  
>>  
>>  
>> -- Motivation
>>  
>> Using enumerate() instead of an (Index, Element) sequence has two main problems.
>> Both arise because enumerate() returns a sequence of (n, Element) tuples,
>> where n is the element *number*, instead of a sequence of (Index, Element).
>>  
>> 1) It doesn't work for collections not indexed by integers.
>>  
>> 2) It doesn't do what you might expect in some cases, as indices do not always start at 0.
>> For example ArraySlice's indices do not: array[2..<5] starts with index 2.
>> Consider the following code to take the 2nd half of the array and remove all empty elements:
>>  
>> var array = [ "", "a", "b", "c", "", "d" ]
>> var secondHalf = array[array.count/2..<array.count]
>> for (index, element) in secondHalf.enumerate() {
>> if element == "" {
>> secondHalf.removeAtIndex(index)
>> }
>> }
>>  
>> This code will crash (ignoring for a moment this should probably be using filter).
>>  
>>  
>>  
>> -- Alternatives
>>  
>> The same effect can already be achieved using the following:
>>  
>> for index in collection.indices {
>>   let element = collection[index]
>>   // ...
>> }
>>  
>> However having a dedicated (Index, Element) sequence has the following advantages:
>> a) It can help prevent people from using enumerate() inappropriately.
>> b) It is very common use case that deserves shortening.
>> c) It can be chained (e.g. to map).
>>  
>>  
>>  
>> -- Proposed Solution
>>  
>> Add a property/method on CollectionType that returns a sequence of (Index, Element) tuples.
>> For example, using a property named indexed:
>>  
>> for (index, element) in collection.indexed {
>>   // ...
>> }
>>  
>> This should be the preferred idiom when you want both the index and the element.
>>  
>> Note that enumerate() does still have valid roles to play:
>> - When you actually do want the element number, not the index.
>> - When you have a SequenceType, as it isn't indexed.
>>  
>>  
>>  
>> -- Implementation
>>  
>> The feature could be entirely implemented using existing constructs:
>>  
>> extension CollectionType {
>>   var indexed: AnySequence<(Index, Generator.Element)> {
>>     return AnySequence(indices.lazy.map { ($0, self[$0]) })
>>   }
>> }
>>  
>> Alternatively, a dedicated SequenceType and/or GeneratorType could be added.
>>  
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution <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/20151228/fc67c4fe/attachment.html>


More information about the swift-evolution mailing list