2009-01-07 5 views
1

Je souhaite renforcer un motif pour qu'il corresponde uniquement aux nombres qui passent une fonction de validation supplémentaire.Pouvez-vous imbriquer un "" contre ""?

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 'One' est le cas facile:

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

L'affaire 'Two' est pas évident pour moi. Il doit valider la somme des nombres. Est-ce que je peux faire ceci sans utiliser de garde?

...

Edit: Je pourrais utiliser un quand-garde (avec une fonction isValid bool-retour) comme ceci:

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

Ceci est moins élégant que correspondant juste un motif; pire, a + b est appliqué deux fois.

Notez également qu'il s'agit d'une version simplifiée de mon code actuel (je n'essaie pas simplement de faire correspondre différentes longueurs de liste par exemple) - la question concerne la correspondance imbriquée sur le modèle double contre.

+0

Je crains don Je ne sais pas ce que vous demandez, et je ne sais pas comment vous voulez valider vos chiffres. Cela aiderait si vous fournissiez un exemple d'entrée et un résultat attendu. – Juliet

+0

Montrez-nous ce que vous pensez de la clause 'quand', nous pourrons vous donner une meilleure réponse. – nlucaroni

Répondre

2

Ma solution: Ajouter un reconnaisseur "helper" avec une valeur de retour conçu pour être utilisé dans le modèle parent:

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

Utilisez comme si:

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

Quand vous faites:

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

Vous êtes correspondant pas nécessairement deux éléments dans une liste. Il est préférable d'utiliser [] au lieu de t pour faire correspondre deux éléments exactement - t peut être une liste de plus d'éléments.

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

Cela permettra d'assurer que vous êtes correspondant à deux et seulement deux éléments --error vérifier gratuitement! Je suggère de faire cela même si vous vous attendez à ce que la fonction accepte uniquement une liste de 0, 1 ou 2 éléments. Ainsi,

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, utiliser when. Ceci est un gâchis. La correspondance de modèle est juste que, l'application de fonctions dans le match n'a vraiment aucun 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 motif correspondra listes de longueur plus alors un si isValid est faux sur le premier motif --be averti. Soyez aussi précis que possible dans vos motifs, si vous voulez correspondre à un élément, faites-le. Si toute opération que vous utilisez pour combiner a et b (dans ce cas +) est coûteuse en termes de calcul, alors vous devrez supprimer le when et utiliser une instruction let avant de tester isValid et de renvoyer le type de variante.

+0

Merci, mais j'ai peut-être simplifié mon vrai code. En fait, je ne veux pas faire correspondre une liste avec seulement 0, 1 ou 2 éléments. Mais c'est toujours complètement hors de propos à la question! –

+0

Désolé, je ne voulais pas paraître si laconique dans mon dernier commentaire! Merci d'avoir pris le temps de m'aider. –