Поделиться через


Шаблоны списка

Заметка

Эта статья является спецификацией компонентов. Спецификация служит проектным документом для функции. Она включает предлагаемые изменения спецификации, а также информацию, необходимую во время проектирования и разработки функции. Эти статьи публикуются до тех пор, пока предложенные изменения спецификации не будут завершены и включены в текущую спецификацию ECMA.

Может возникнуть некоторое несоответствие между спецификацией компонентов и завершенной реализацией. Эти различия фиксируются в соответствующих собраниях по проектированию языка (LDM).

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

Проблема чемпиона: https://github.com/dotnet/csharplang/issues/3435

Сводка

Позволяет сопоставить массив или список с последовательностью шаблонов, например, array is [1, 2, 3] будет соответствовать целочисленному массиву длиной три с элементами 1, 2, 3, соответственно.

Подробный дизайн

Синтаксис шаблона изменяется следующим образом:

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
  ;

Существует два новых шаблона:

  • list_pattern используется для сопоставления элементов.
  • slice_pattern допускается один раз и только непосредственно в list_pattern_clause и отбрасывает ноль или более элементов.

Совместимость шаблонов

list_pattern совместим с любым типом, который исчисляемый, а также индексируемый — он имеет доступный индексатор, который принимает Index в качестве аргумента или, иначе, доступный индексатор с одним параметром int. Если оба индексатора присутствуют, предпочтителен первый.

slice_pattern с подпаттерном совместим с любым типом, который , а также среза — он имеет доступный индексатор, который принимает Range в качестве аргумента или иным образом доступный метод Slice с двумя параметрами int. Если оба присутствуют, то первый предпочтителен.

slice_pattern без подпаттерна совместим с любым типом, совместимым с list_pattern.

Этот набор правил является производным от шаблона индексатора диапазона .

Проверка вхождения

Проверка подстановки работает так же, как и позиционные шаблоны с ITuple — соответствующие подпаттерны сопоставляются по позиции с добавлением дополнительного узла для проверки длины.

Например, следующий код выдает ошибку, так как оба шаблона дают один и тот же DAG:

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

В отличие от:

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

Порядок сопоставления подпаттернов во время выполнения не определен, и неудавшееся сопоставление может не пытаться сопоставить все подпаттерны.

Учитывая определенную длину, возможно, что два подпаттерна ссылаются на один и тот же элемент, в этом случае в решение DAG вставляется тест для этого значения.

  • Например, [_, >0, ..] or [.., <=0, _] становится length >= 2 && ([1] > 0 || length == 3 || [^2] <= 0), где значение длины 3 подразумевает другой тест.
  • И наоборот, [_, >0, ..] and [.., <=0, _] становится length >= 2 && [1] > 0 && length != 3 && [^2] <= 0, когда значение длины 3 запрещает другой тест.

В результате ошибка возникает для чего-то вроде case [.., p]: case [p]:, так как во время выполнения мы сопоставляем тот же элемент во втором случае.

Если подпаттерн среза соответствует списку или значению длины, подпаттерны обрабатываются как если бы они были прямым подпаттерном содержащего списка. Например, [..[1, 2, 3]] включает шаблон вида [1, 2, 3].

Ниже приведены предположения об используемых элементах:

  • Свойство, которое делает тип числом, предполагается всегда возвращать неотрицательное значение, если и только если тип индексируемым. Например, шаблон { Length: -1 } никогда не может соответствовать массиву.
  • Предполагается, что элемент, который делает тип нарезаемым, работает должным образом, то есть возвращаемое значение никогда не равно null, и он представляет собой правильный подсрез содержащего списка.

Поведение операции сопоставления шаблонов не определено, если какие-либо из приведенных выше предположений не выполняются.

Снижение

Шаблон формы expr is [1, 2, 3] эквивалентен следующему коду:

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

slice_pattern действует как правильное игнорирование, т. е. никакие тесты не будут генерироваться для такого шаблона, оно влияет только на другие узлы, а именно длину и индексатор. Например, шаблон формы expr is [1, .. var s, 3] эквивалентен следующему коду (если он совместим с помощью явных Index и поддержки 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

Тип ввода для шаблона среза является типом возврата базового метода this[Range] или Slice с двумя исключениями: для string используется string.Substring, а для массивов — RuntimeHelpers.GetSubArray.

Неразрешенные вопросы

  1. Следует ли поддерживать многомерные массивы? (ответ [LDM 2021-05-26]: не поддерживается. Если мы хотим сделать общий выпуск MD-массива, мы должны пересмотреть все области, в которых они в настоящее время испытывают недостаток, а не только шаблоны списков.)
  2. Следует ли принять общий шаблон после .. в slice_pattern? (ответ [LDM 2021-05-26]: Да, любой шаблон разрешен после среза.)
  3. По этому определению шаблон [..] тестирует expr.Length >= 0. Следует ли опустить такой тест, предполагая, что Length всегда не является отрицательным? (ответ [LDM 2021-05-26]: [..] не будет выполнять проверку длины)