2

I am trying to understand why the .. operator works the way it does, for example:

var data = new []{0,1,2,3,4,5,6,7,8,9,10};
var test = data[1..4]; // This returns array with 1,2,3 

Logically, I would assume the result would be either 1,2,3,4 or 2,3 (if the last index isn't included then the first one shouldn't also)

or

var test = data[0..]; // This returns array with 0,1,2,3,4,5,6,7,8,9,10 (zero at index 0 is also included)
var test = data[^0..]; // This returns array with nothing,  where i would expect 10, since 10 is at index zero if we traverse the array backwards

I know there must be a reason why it was designed to work like that, but I can't seem to figure it out, so what is the purpose of this behavior?

Thank you.

Askerman
  • 787
  • 1
  • 12
  • 31
  • 1
    "if the last index isn't included then the first one shouldn't also" - why? Using half-closed intervals is not *that* uncommon. – Damien_The_Unbeliever Dec 30 '20 at 11:05
  • c# starts arrays with index zero (not index one). – jdweng Dec 30 '20 at 11:05
  • 1
    Half open ranges like this are all over the place if you know where to look. I would say that the kind where both the start and the end are excluded are the *least* common. – Sweeper Dec 30 '20 at 11:07
  • 2
    See this question in other languages: [1](https://www.quora.com/Why-are-Python-ranges-half-open-exclusive-instead-of-closed-inclusive), [2](https://stackoverflow.com/questions/9963401/why-are-standard-iterator-ranges-begin-end-instead-of-begin-end), [3](https://stackoverflow.com/questions/11364533/why-are-slice-and-range-upper-bound-exclusive). – Sweeper Dec 30 '20 at 11:09

2 Answers2

3

Why is the end index not included, but the start index is?

This is known as a half open range, and there are already questions asking about this in Python and C++. Essentially, the main advantage of this is that:

  • The length of the range is exactly (end - start).
  • You don't need to add/minus 1 as much in range-based algorithms
  • To slice something in half at an index, you can use the same index: x[..i] gives you the first half, and x[i..] gives you the second half. i.e. x[..i] concatenated with i[i..] is equal to x itself.
  • If a range's end is equal to another's start, the two ranges are immediately next to each other, and no overlapping.

Why is data[^0..] empty?

This is documented clearly. ^n means Length - n, so ^0 means data.Length - 0 here, which is just data.Length. data[data.Length..] is clearly empty.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
0

It's simple enough. Consider the following:

var data = new []{0,1,2,3,4,5,6,7,8,9,10};
var test = data[0..10];

The 0 to 10, implies that it will select 11 items, if the last was inclusive. You have to start at 0, as C# indexes are zero based, and you'd need a -1 to include the first, if they were both exlusive.

As mentioned in the documentation:

A range specifies the start and end of a range. Ranges are exclusive, meaning the end isn't included in the range. The range [0..^0] represents the entire range, just as [0..sequence.Length] represents the entire range.

The data[^0..]; statements means, starting from the 0 position, bring whatever is below it. The ^ operator works inversly for the same reasons. (^0 is not inclusive of 0)

Athanasios Kataras
  • 25,191
  • 4
  • 32
  • 61