Pouvez-vous imbriquer un & double? # 8220; contre & # 8221; correspondance de modèle?
-
05-07-2019 - |
Question
Je souhaite renforcer un modèle pour qu'il ne corresponde qu'aux nombres réussissant une fonction de validation supplémentaire.
let (|IsValid|_|) n = ...
let (|Nil|One|Two|) (l : int list) =
match l with
| a :: b :: t -> Two(a + b)
| a :: t -> One(a)
| _ -> Nil
Le cas "One" est simple:
| IsValid(a) :: t -> One(a)
Le cas "Deux" ne m'est pas évident. Il doit valider la somme des nombres. Puis-je faire cela sans utiliser de garde-minute?
...
Éditer: Je pourrais utiliser un when-guard (avec une fonction isValid renvoyant booléen) comme ceci:
| a :: b :: t when isValid a + b -> Two(a + b)
C’est moins élégant que de simplement faire correspondre un motif; pire, a + b est appliqué deux fois.
Notez également qu’il s’agit d’une version simplifiée de mon code actuel (par exemple, je n’essaie pas de faire correspondre des longueurs de liste différentes) - la question concerne la correspondance imbriquée sur le modèle de double cons.
La solution
Ma solution: ajouter un "helper". reconnaisseur avec une valeur de retour conçue pour être utilisée dans le modèle parent:
let (|MatchTwo|_|) = function
| a :: b :: t -> Some(a + b :: t)
| _ -> None
Utilisez-le comme suit:
let (|Nil|One|Two|) (l : int list) =
match l with
| MatchTwo(IsValid(a) :: t) -> Two(a)
| IsValid(a) :: t -> One(a)
| _ -> Nil
Autres conseils
Quand vous le faites:
| a :: b :: t -> ...
Vous ne faites pas nécessairement correspondre deux éléments dans une liste. Il vaut mieux utiliser []
au lieu de t
pour faire correspondre deux éléments à la perfection - t
peut constituer une liste de plusieurs éléments.
| a :: b :: [] -> Two (a+b)
Cela garantira que vous faites correspondre deux et seulement deux éléments - vérification de l'erreur gratuitement! Je suggère de le faire même si vous vous attendez à ce que la fonction accepte uniquement une liste de 0, 1 ou 2 éléments. Alors,
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
Oui, utilisez lorsque
. Ceci est un gâchis. La correspondance de modèle est juste cela, appliquer des fonctions dans le match n'a pas de sens. Mais tenez compte de ce que j'ai déjà mentionné. Sur la base de votre exemple:
match l with
| a :: b :: t when isValid (a+b) -> Two (a+b)
| a :: t when isValid (a) -> One a
| _ -> Nil
Le deuxième modèle correspondra à des listes de longueur supérieure à un si isValid est faux sur le premier modèle --être prévenu. Soyez aussi précis que possible dans vos modèles. Si vous voulez faire correspondre un élément, faites-le.
Si l'opération que vous utilisez pour combiner a et b (+ dans ce cas +) est coûteuse en calcul, vous devrez alors supprimer lorsque
et utiliser une instruction let avant de tester isValid et de renvoyer l'élément type de variante.