Почему новый индекс оператора шляпы из функции нарезки массива C # 8 не начинается с 0?

156

C # 8.0 представляет удобный способ нарезки массивов - см. Официальный пост C # 8.0 .

Синтаксис для доступа к последнему элементу массива

int value[] = { 10, 11, 12, 13 };

int a = value[^1]; // 13
int b = value[^2]; // 12

Мне интересно, почему индексирование для доступа к элементам в обратном направлении начинается с 1 вместо 0? Есть ли техническая причина для этого?

Майкл Питтино
источник
11
Обратите внимание, что диапазоны C ++ также [beginInclusive, endExclusive). Это общее соглашение.
взрыв
3
@Sinatr: Исходя из этого сообщения в блоге, синтаксис для возврата всего будет value[0..^0], так как конечный индекс является эксклюзивным (так работает и большинство других языков). Также удобно value[^i..^0]дадут вам последние iпредметы.
BlueRaja - Дэнни Пфлюгофт
1
@bommelding: C ++ в rbegin()некоторой степени не согласен с этим понятием - первый элемент из этого диапазона также не является зацикленным . ;-)
DevSolar
1
О, круто, это выглядит как эквивалент отрицательной индексации в python:value[-1] # 13
cs95
1
@coldspeed А в Ruby так же, как в Python. Я предполагаю, что они оба позаимствовали это соглашение у Perl.
Уэйн Конрад

Ответы:

170

Официальный ответ

Для лучшей наглядности вот комментарий от Мэдс Торгерсен, объясняющий это дизайнерское решение из блога C # 8 :

Мы решили следовать Python, когда дело доходит до арифметики начала и конца. 0 обозначает первый элемент (как всегда) и  ^0 «длина», т. е. один с самого конца. Таким образом, вы получите простое отношение, где позиция элемента от начала плюс его позиция от конца равна длине. x в  ^x то , что вы бы вычесть из длины , если бы вы сделали вычислить число самостоятельно.

Почему бы не использовать минус ( -) вместо нового ^оператора hat ( )? Это в первую очередь связано с диапазонами. Опять же, в соответствии с Python и большей частью отрасли, мы хотим, чтобы наши диапазоны были включающими в начале, исключительными в конце. Какой индекс вы передаете, чтобы сказать, что диапазон должен идти до конца? В C # ответ прост: x..^0идет от  x конца до конца. В Python нет явного индекса, который вы можете дать: -0не работает, потому что он равен 0первому элементу! Таким образом , в Python, вы должны оставить конечный индекс от полностью выразить диапазон , который идет до конца: x... Если вычисляется конец диапазона, то вам нужно помнить, чтобы иметь особую логику на случай, если он выйдет 0. Как x..-yи гдеyбыл вычислен и вышел 0. Это распространенная неприятность и источник ошибок.

Наконец, обратите внимание, что индексы и диапазоны являются первоклассными типами в .NET / C #. Их поведение не связано с тем, к чему они применяются, или даже для использования в индексаторе. Вы можете полностью определить свой собственный индексатор, который использует Index, и другой, который принимает Range- и мы собираемся добавить такие индексаторы, например  Span. Но вы также можете иметь методы, которые принимают диапазоны, например.

Мой ответ

Я думаю, что это соответствует классическому синтаксису, к которому мы привыкли:

value[^1] == value[value.Length - 1]

Если бы он использовал 0, было бы непонятно, когда два синтаксиса использовались бок о бок. Таким образом, у него снижается когнитивная нагрузка.

Другие языки, такие как Python, также используют то же соглашение.

Мартин Зикмунд
источник
13
Небольшая поправка к комментарию Мэдса: вам не нужно полностью исключать конечный индекс в python. Вы можете использовать Noneвместо ряда: [0,1,2,3,4][2:None] == [2,3,4]. Но да, вы не можете использовать целое число в качестве конечного индекса (без вычисления длины, очевидно).
Джакомо Альзетта
4
Подожди .. что не так с x..? Это выглядит нормально, и у меня никогда не было проблем с [3:]синтаксисом python .
косилка
8
@mowwwalker - разве это не описано в цитате? «Итак, в Python ... Если вычисляется конец диапазона, вам нужно помнить, чтобы иметь специальную логику на случай, если она выйдет в 0»
Damien_The_Unbeliever
3
Комментарий @mowwwalker Mads касался случаев, когда вы не знаете, каким будет значение индекса, потому что оно каким-то образом вычисляется. Они говорят, что в случае, если вы хотите вычислить endIndexкак отрицательный индекс (то есть индекс с конца), вы будете иметь разрыв между отрицательными и положительными числами, потому что 0не будет работать правильно в этом случае. Как я отметил, что вам нужно заменить 0на Noneдля этого. Это означает, что ваш код должен выглядеть, seq[startIndex:endIndex or None]например. Значение or Noneдолжно быть опущено, если endIndexожидается, что оно будет положительным.
Джакомо Альзетта
5
Приятно видеть, что они не повторяют ошибку Питона с -0. Работа с этим особым случаем - огромная проблема, и ее слишком легко забыть.
user2357112 поддерживает Monica