Можете ли вы вложить соответствие шаблону “двойные минусы”?

StackOverflow https://stackoverflow.com/questions/420832

  •  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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top