Can you nest a double minus pattern?

I want to strengthen the pattern to only match numbers that pass an additional validation function.

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

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

      

Case One is easy:

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

      

Case "Two" is not obvious to me. He has to check the sum of the numbers. Can I do this without using a safety device?

...

Edit: I could use if-guard (with bUL-return isValid function) like this:

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

      

This is less elegant than just pattern matching; worse, a + b is applied twice.

Also note that this is a simplified version of my actual code (e.g. I'm not trying to just match different lengths of a list) - a question about nested pattern matching double cons.

+1


source to share


2 answers


My solution: add a "helper" resolver with a return value intended to be used in the parent template:

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

      



Use it like this:

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

      

+2


source


When you do:

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

      

You are not necessarily matching two items in the list. Better to use []

instead t

to exactly match two elements - t

can be a list of more elements.

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

      

This will ensure that two and only two elements match - a failsafe test! I suggest doing this even if you expect the function to only accept a list of 0, 1, or 2 elements. Thus,

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

      

Yes, use it when

. It's a mess. Pattern matching is that applying functions within a match really doesn't make sense. But consider what I mentioned earlier. Based on your example:

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

      

The second pattern will match lists of length longer than one, if isValid is false for the first pattern, it will be warned. Be as specific as possible in your templates, if you want to combine one element, do so.

If any operation you use to combine a and b (in this case +) is computationally expensive, you will have to forgo when

and use the let statement before testing isValid and returning the variant type.

+2


source







All Articles