Можете ли вы вложить соответствие шаблону “двойные минусы”?
-
05-07-2019 - |
Вопрос
Я хочу усилить шаблон, чтобы он соответствовал только тем числам, которые передают дополнительную функцию проверки.
let (|IsValid|_|) n = ...
let (|Nil|One|Two|) (l : int list) =
match l with
| a :: b :: t -> Two(a + b)
| a :: t -> One(a)
| _ -> Nil
Случай "Один" - это просто:
| IsValid(a) :: t -> One(a)
Случай "Два" для меня не очевиден.Он должен подтвердить сумму чисел.Могу ли я сделать это без использования when-guard?
...
Редактировать:Я мог бы использовать when-guard (с функцией isValid, возвращающей значение bool) следующим образом:
| a :: b :: t when isValid a + b -> Two(a + b)
Это менее элегантно, чем просто сопоставление шаблона;хуже того, a + b применяется дважды.
Также обратите внимание, что это упрощенная версия моего фактического кода (например, я не пытаюсь просто сопоставлять список разной длины) - вопрос заключается во вложенном сопоставлении по шаблону double cons.
Решение
Мое решение:Добавьте "вспомогательный" распознаватель с возвращаемым значением, предназначенным для использования в родительском шаблоне:
let (|MatchTwo|_|) = function
| a :: b :: t -> Some(a + b :: t)
| _ -> None
Используйте это вот так:
let (|Nil|One|Two|) (l : int list) =
match l with
| MatchTwo(IsValid(a) :: t) -> Two(a)
| IsValid(a) :: t -> One(a)
| _ -> Nil
Другие советы
Когда вы делаете:
| a :: b :: t -> ...
Вам не обязательно сопоставлять два элемента в списке.Это лучше использовать []
вместо того , чтобы t
чтобы точно соответствовать двум элементам --t
может представлять собой список из большего количества элементов.
| a :: b :: [] -> Two (a+b)
Это гарантирует, что вы сопоставляете два и только два элемента - проверка ошибок бесплатна!Я предлагаю сделать это, даже если вы ожидаете, что функция будет принимать список только из 0, 1 или 2 элементов.Итак,
Редактировать:
let (|MatchTwo|_|) = function
| a :: b :: t -> Some(a + b :: t)
| _ -> None
let (|Nil|One|Two|) (l : int list) = match l with
| MatchTwo(IsValid(a) :: t) -> Two(a)
| IsValid(a) :: t -> One(a)
| _ -> Nil
Да, используй when
.Это полный бардак.Сопоставление с шаблоном - это просто то, что применение функций внутри сопоставления действительно не имеет смысла.Но примите во внимание то, о чем я упоминал ранее.Основываясь на вашем примере:
match l with
| a :: b :: t when isValid (a+b) -> Two (a+b)
| a :: t when isValid (a) -> One a
| _ -> Nil
Второй шаблон будет соответствовать спискам длиной больше единицы, если значение isValid равно false для первого шаблона - имейте в виду.Будьте как можно более конкретны в своих шаблонах, если вы хотите сопоставить один элемент, сделайте это.
Если любая операция, которую вы используете для объединения a и b (в данном случае +), требует больших вычислительных затрат, то вам придется отказаться от when
и используйте оператор let перед тестированием isValid и возвращением типа variant.