Pergunta

Eu quero fortalecer um padrão para corresponder apenas os números que passam uma função de validação adicional.

let (|IsValid|_|) n = ...

let (|Nil|One|Two|) (l : int list) =
    match l with 
    | a :: b :: t -> Two(a + b)
    | a :: t      -> One(a)
    | _           -> Nil

O 'One' caso é fácil:

    | IsValid(a) :: t -> One(a)

O caso 'Two' não é óbvio para mim. Ele precisa validar a soma dos números. Posso fazer isso sem usar um quando-guarda?

...

Edit: Eu poderia usar um quando-guarda (com uma função isValid-retornar bool) como este:

    | a :: b :: t when isValid a + b -> Two(a + b)

Este é menos elegante do que apenas combinando um padrão; pior, a + b é aplicado duas vezes.

Observe também esta é uma versão simplificada do meu código real (não estou tentando combinar simplesmente contra diferentes comprimentos de lista, por exemplo) - a questão é sobre a correspondência aninhada sobre o padrão contras de casal.

Foi útil?

Solução

A minha solução: Adicionar um reconhecedor "helper", com um valor de retorno projetado para ser usado no padrão parent:

let (|MatchTwo|_|) = function
    | a :: b :: t -> Some(a + b :: t)
    | _ -> None

Use-o assim:

let (|Nil|One|Two|) (l : int list) =
    match l with 
    | MatchTwo(IsValid(a) :: t) -> Two(a)
    |          IsValid(a) :: t  -> One(a)
    | _                         -> Nil

Outras dicas

Quando você faz:

| a :: b :: t -> ... 

Você não são necessariamente combinar dois elementos em uma lista. É melhor usar [] vez de t para combinar dois elementos exatamente --t pode ser uma lista de mais elementos.

 | a :: b :: [] -> Two (a+b)

Isto irá assegurar que você está combinando dois e apenas dois elementos --error verificação de graça! Sugiro fazer isso mesmo que você espera que a função para aceitar apenas uma lista de 0, 1 ou 2 elementos. Então,

EDIT:

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

Sim, uso when. Esta é uma bagunça. correspondência de padrões é apenas isso, aplicando funções dentro do jogo realmente não faz sentido. Mas ter em conta o que eu já mencionei antes. Com base no seu exemplo:

match l with
| a :: b :: t when isValid (a+b) -> Two (a+b)
| a :: t when isValid (a) -> One a
| _ -> Nil

O segundo padrão irá corresponder listas de comprimento mais longo, em seguida, um se isValid é falsa no primeiro padrão --be advertiu. Seja o mais específico em seus padrões quanto possível, se você quer dizer para combinar um elemento, fazê-lo.

Se qualquer que seja a operação que você usa para combinar a e b (neste caso, +) é computacionalmente caro, então você vai ter que largar o when e usar uma instrução let antes de testar o isValid e retornando o tipo de variante.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top