Compartir a través de


Patrones de lista

Nota

Este artículo es una especificación de características. La especificación actúa como documento de diseño de la característica. Incluye cambios de especificación propuestos, junto con la información necesaria durante el diseño y el desarrollo de la característica. Estos artículos se publican hasta que se finalizan los cambios de especificación propuestos e se incorporan en la especificación ECMA actual.

Puede haber algunas discrepancias entre la especificación de características y la implementación completada. Esas diferencias se recogen en las notas de la reunión de diseño de lenguaje (LDM) correspondientes.

Puede obtener más información sobre el proceso de adopción de especificaciones de características en el estándar del lenguaje C# en el artículo sobre las especificaciones de .

Problema planteado por el experto: https://github.com/dotnet/csharplang/issues/3435

Resumen

Permite hacer coincidir una matriz o una lista con una secuencia de patrones, por ejemplo, array is [1, 2, 3] coincidirán con una matriz de enteros de la longitud tres con 1, 2, 3 como elementos, respectivamente.

Diseño detallado

La sintaxis del patrón se modifica de la siguiente manera:

list_pattern_clause
  : '[' (pattern (',' pattern)* ','?)? ']'
  ;

list_pattern
  : list_pattern_clause simple_designation?
  ;

slice_pattern
  : '..' pattern?
  ;

primary_pattern
  : list_pattern
  | slice_pattern
  | // all of the pattern forms previously defined
  ;

Hay dos nuevos patrones:

  • El list_pattern se usa para buscar coincidencias con elementos.
  • Un slice_pattern se permite solo una vez y directamente en una list_pattern_clause, descartando cero o más elementos .

Compatibilidad de patrones

Un list_pattern es compatible con cualquier tipo que sea contable e indexable; tiene un indexador accesible que toma un Index como argumento o, alternativamente, un indexador accesible con un único parámetro int. Si ambos indexadores están presentes, se prefiere el primero.

Un slice_pattern con un subpatrón es compatible con cualquier tipo que sea tanto contable como divisible; tiene un indexador accesible que toma un Range como argumento o tiene un método Slice accesible con dos parámetros int. Si ambos están presentes, se prefiere el primero.

Un slice_pattern sin un subpatrón es compatible con cualquier tipo que sea compatible con un list_pattern.

Este conjunto de reglas se deriva del patrón de indexación de rango de .

Comprobación de subsumpción

La comprobación de subsumpción funciona igual que los patrones posicionales con ITuple; los subpatrones correspondientes se cotejan por posición, más un nodo adicional para comprobar la longitud.

Por ejemplo, el código siguiente genera un error porque ambos patrones producen el mismo DAG:

case [_, .., 1]: // expr.Length is >= 2 && expr[^1] is 1
case [.., _, 1]: // expr.Length is >= 2 && expr[^1] is 1

A diferencia de:

case [_, 1, ..]: // expr.Length is >= 2 && expr[1] is 1
case [.., 1, _]: // expr.Length is >= 2 && expr[^2] is 1

El orden en el que se coinciden los subpatrones en tiempo de ejecución no está especificado y es posible que una coincidencia fallida no intente hacer coincidir todos los subpatrones.

Dada una longitud específica, es posible que dos subpatrones hagan referencia al mismo elemento, en cuyo caso se inserta una prueba para este valor en el DAG de decisión.

  • Por ejemplo, [_, >0, ..] or [.., <=0, _] se convierte en length >= 2 && ([1] > 0 || length == 3 || [^2] <= 0) donde el valor de longitud de 3 implica la otra prueba.
  • Por el contrario, [_, >0, ..] and [.., <=0, _] se convierte en length >= 2 && [1] > 0 && length != 3 && [^2] <= 0 donde el valor de longitud de 3 no permite la otra prueba.

Como resultado, se produce un error para algo como case [.., p]: case [p]: ya que, en tiempo de ejecución, estamos cotejando con el mismo elemento en el segundo caso.

Si un subpatrón de segmento coincide con una lista o un valor de longitud, los subpatrones se tratan como si fueran un subpatrón directo de la lista contenedora. Por ejemplo, [..[1, 2, 3]] engloba un patrón de la forma [1, 2, 3].

Las siguientes suposiciones se aplican a los miembros que se están utilizando:

  • Se supone que la propiedad que hace que el tipo sea contable devuelve siempre un valor no negativo solo si el tipo es indexable. Por ejemplo, el patrón { Length: -1 } nunca puede coincidir con una matriz.
  • Se supone que el miembro que hace que el tipo sea divisible en segmentos se comporte bien, es decir, el valor devuelto nunca es nulo y que es un subsegmento adecuado de la lista contenedora.

El comportamiento de una operación de coincidencia de patrones no está definido si alguno de los supuestos anteriores no se cumple.

Reducción

Un patrón del formulario expr is [1, 2, 3] es equivalente al código siguiente:

expr.Length is 3
&& expr[new Index(0, fromEnd: false)] is 1
&& expr[new Index(1, fromEnd: false)] is 2
&& expr[new Index(2, fromEnd: false)] is 3

Un slice_pattern actúa como un descarte adecuado, es decir, no se emitirá ninguna prueba para este patrón, sino que solo afecta a otros nodos, es decir, la longitud y el indexador. Por ejemplo, un patrón de la forma expr is [1, .. var s, 3] es equivalente al siguiente código (si es compatible explícitamente mediante Index y Range).

expr.Length is >= 2
&& expr[new Index(0, fromEnd: false)] is 1
&& expr[new Range(new Index(1, fromEnd: false), new Index(1, fromEnd: true))] is var s
&& expr[new Index(1, fromEnd: true)] is 3

El tipo de entrada para slice_pattern es el tipo de valor devuelto del método subyacente this[Range] o Slice, con dos excepciones: para string y para matrices, se usarán string.Substring y RuntimeHelpers.GetSubArray, respectivamente.

Preguntas sin resolver

  1. ¿Deberíamos admitir matrices multidimensionales? (respuesta [LDM 26-05-2021]: No compatible. Si queremos hacer un lanzamiento general centrado en matrices MD, queremos revisar todas las áreas en las que faltan actualmente, no solo los patrones de lista).
  2. ¿Deberíamos aceptar un patrón general tras .. en un slice_pattern? (respuesta [LDM 2021-05-26]: Sí, se permite cualquier patrón después de un segmento).
  3. Por esta definición, el patrón [..] verifica expr.Length >= 0. ¿Deberíamos omitir dicha prueba, suponiendo que Length siempre no es negativo? (respuesta [LDM 26-05-2021]: [..] no emitirá una comprobación de longitud)