Range counting from the end of an array

In numerical analysis, it is very common to want to define a range that goes up to the n-th element counted from the end of the array. In traditional Matlab colon notation, x[1:end-5] would mean the elements x[1], x[2], ... x[N-5] where N would be the length of x. The very popular Python numpy package has an even terser notation for that: x[1:-5].

Swift 4 introduced one-sided ranges, and that's great, but it still lacks that very useful way of expressing slices. That would clearly require a non-trivial change to the API of ranges and arrays, as they need to communicate about the size of the latter to generate the range elements. Besides there is a syntactic issue as ... -5 is illegal: one would need to do ...(-5) which is horrible. Perhaps the correct behaviour could be obtained without parentheses with a new operator ...-. Clearly, a lot of non-trivial issues to sort out.

Would there be any interest for that? I mean, is it worth trying to investigate further, or is there zero chances for something like that to be accepted ever?

3 Likes

Yes very much. See this proposal which proposes a similar solution to what you've proposed here.

I missed that, thanks! Looks like a straightforward numpy-style notation does not have the favour of the locals. But better contribute to that thread then…

I'm going to push back on this just a little bit; what's common in numerical analysis (in my experience) is to split an array or matrix into "a block of the first / last N elements / columns / rows" and "the rest of the array / matrix". Matlab/python-style indexing is one way to express that, but it's not the only way, nor obviously the best way.

Personally, I really like the python way of doing this (which is a general python thing, by the way, not specific to numpy) because it's terse and familiar to me, but I wouldn't want to bake it into Swift without exploring some other ways tiling like this can be expressed.

1 Like

I agree. Once exposed to the Python way, you can't forget it.

I have used many languages (I write science software) that use negative numbers to mean count from the end, a la Python, and always found them to be a source of out-by-one errors. In Swift at the moment if you go off the index limits, either high or low, you get an error. If we change to allowing negative indices we loose half the checking!

I think a better solution would be to overload subscript, e.g. x[fromLast: 0] to mean the last element of an array. Note 0, not 1, to be consistent with indexing starting at 0.

Note in many scientific languages, e.g. Matlab, arrays are indexed from 1 and 0 is an error. The out-by-one error is not as bad in these languages because of 0, but if you are striding the array you still have the problem.

−1 makes perfect sense if you think of an array broken into many slices. The item at index −1 in one array is also the item “conceptually” at index −1 in the slice after it.

Not saying that -1 is hard to understand, just that it has proved to be a source of errors.

Did those errors actually occur when using a literal? Imho it's not that dangerous to have array[-1] return the last element — but when a variable is used for indexing, negative numbers imho would cause big trouble.

@Tino The errors occurred when calculating and/or stepping index, I agree that literals are likely harmless.