Pode você um ninho “contras duplas” padrão de jogo?
-
05-07-2019 - |
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.
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.