¿Por qué el nuevo índice de operador de sombrero de la función de corte de matriz C # 8 comienza en 0?

156

C # 8.0 presenta una forma conveniente de cortar matrices: consulte la publicación de blog oficial de C # 8.0 .

La sintaxis para acceder al último elemento de una matriz es

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

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

Me pregunto por qué la indexación para acceder a los elementos al revés comienza en 1 en lugar de 0. ¿Hay alguna razón técnica para esto?

Michael Pittino
fuente
11
Tenga en cuenta que los rangos de C ++ también lo son [beginInclusive, endExclusive). Es una convención común.
bommelding
3
@Sinatr: Basado en esa publicación de blog, la sintaxis para devolver todo sería value[0..^0], ya que el índice final es exclusivo (que es como funcionan también la mayoría de los otros idiomas). Además, convenientemente, value[^i..^0]le dará los últimos iartículos.
BlueRaja - Danny Pflughoeft
1
@bommelding: C ++ rbegin()no está de acuerdo con esa noción: el primer elemento fuera de ese rango tampoco es el que está más allá del final. ;-)
DevSolar
1
Oh, genial, esto parece el equivalente de la indexación negativa en python:value[-1] # 13
cs95
1
@coldspeed Y en Ruby lo mismo que Python. Supongo que ambos tomaron prestada esta convención de Perl.
Wayne Conrad el

Respuestas:

170

Respuesta oficial

Para una mejor visibilidad, aquí hay un comentario de Mads Torgersen que explica esta decisión de diseño de la publicación del blog C # 8 :

Decidimos seguir Python cuando se trata de la aritmética desde el principio y desde el final. 0 designa el primer elemento (como siempre) y  ^0 el elemento "longitud", es decir, el que está justo al final. De esa manera, obtienes una relación simple, donde la posición de un elemento desde el principio más su posición desde el final es igual a la longitud. el x  en ^x  es lo que se ha restado de la longitud si había hecho sus propios cálculos.

¿Por qué no utilizar el operador menos ( -) en lugar del nuevo ^operador hat ( )? Esto tiene que ver principalmente con los rangos. Nuevamente, de acuerdo con Python y la mayoría de la industria, queremos que nuestras gamas sean inclusivas al principio, exclusivas al final. ¿Cuál es el índice que pasa para decir que un rango debe llegar hasta el final? En C # la respuesta es simple:x..^0 va desde  x el final. En Python, no hay un índice explícito que pueda dar: ¡ -0no funciona, porque es igual al 0primer elemento! Así que en Python, usted tiene que dejar el índice final por completo para expresar una gama que va hasta el final: x... Si se calcula el final del rango, debe recordar tener una lógica especial en caso de que salga 0. Como en x..-ydondeyse calculó y salió a 0. Esta es una molestia común y fuente de errores.

Finalmente, tenga en cuenta que los índices y rangos son tipos de primera clase en .NET / C #. Su comportamiento no está vinculado a lo que se les aplica, o incluso para ser utilizado en un indexador. Puede definir totalmente su propio indexador que toma Index y otro que toma Range, y vamos a agregar dichos indexadores, por ejemplo  Span. Pero también puede tener métodos que toman rangos, por ejemplo.

Mi respuesta

Creo que esto es para que coincida con la sintaxis clásica a la que estamos acostumbrados:

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

Si usara 0, sería confuso cuando las dos sintaxis se usaran una al lado de la otra. De esta manera tiene menor carga cognitiva.

Otros lenguajes como Python también usan la misma convención.

Martin Zikmund
fuente
13
Corrección menor al comentario de Mads: no es necesario dejar completamente el índice final en Python. Se puede utilizar Noneen lugar de un número: [0,1,2,3,4][2:None] == [2,3,4]. Pero sí, no puede usar un número entero como índice final (sin calcular la longitud obviamente).
Giacomo Alzetta
44
Espera ... ¿qué pasa x..? Eso parece estar bien y nunca he tenido problemas con la [3:]sintaxis de Python .
mowwwalker
8
@mowwwalker: ¿no está eso ya cubierto en la cita? "Entonces, en Python ... Si se calcula el final del rango, entonces debes recordar tener una lógica especial en caso de que salga a 0"
Damien_The_Unbeliever
3
El comentario de @mowwwalker Mads fue sobre casos en los que no sabes cuál será el valor del índice porque se calcula de alguna manera. Están diciendo que en el caso de que desee calcular endIndexcomo un índice negativo (es decir, un índice desde el final) tendrá una discontinuidad entre los números negativos y positivos porque 0no funcionará de la manera correcta en ese caso. Como señalé, tienes que reemplazar 0por Noneeso. Esto significa que su código debería verse seq[startIndex:endIndex or None]por ejemplo. Se or Nonedebe omitir si endIndexse espera que sea positivo.
Giacomo Alzetta
55
Es bueno ver que no repiten el error de Python con la cosa -0. Manejar ese caso especial es una molestia enorme y demasiado fácil de olvidar.
user2357112 es compatible con Monica el