Pouvez-vous imbriquer un & double? # 8220; contre & # 8221; correspondance de modèle?

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

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

Était-ce utile?

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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top