2
votes

Say I have lists like [1;2;3;4;5;6;9] and [1;2;3;9] and I want to write a pattern which captures lists which begin with 1 and end with 9, and also capture the values of the middle of the list. Is this possible to do with OCaml's pattern matching?

I've tried to write something like

match l with
| 1::middle::9

or

match l with
| 1::middle::9::[]

but I'm not sure that these are doing what I want, and are probably instead only matching 3 element lists. Is there an approach I can take to match things like this? Should I be using nested pattern matches?

2

2 Answers

3
votes

There's no pattern that matches the end of a list, so there's no pattern like what you want. You can do two matches:

match l with
| 1 :: _ -> (
    match List.rev l with
    | 9 :: _ -> true
    | _ -> false
)
| _ -> false

Finding the end of a list is a linear time operation. If your lists can be long, you might want to use a different data structure.

0
votes

If you're just making checks on the first and last elements of a list, you may want to use conditional statements instead of pattern matching:

let is_valid l = 
    let open List in
    let hd' = hd l in (* Get the first element of the list *)
    let tl' = rev l |> hd in (* Get the last element of the list *)
    if hd' = 1 && tl' = 9 then true else false

    is_valid [1;2;3;4;5;6;9] (* bool = true *)

However, if you are trying to extract that middle pattern it may be worthwhile to use pattern matching. We can do something similar to what Jeffery suggested because of the reason he pointed out (pattern matching can't match the end of a list):

let is_valid l =
    let open List in
    match l with
    | 1 :: mid ->  (* `mid` holds list without the `1` *)
        (match rev mid with (* `rev_mid` holds list without the 9 but reversed *)
        | 9 :: rev_mid -> Some (rev rev_mid) (* reverse to get correct order *)
        | _ -> None)
    | _ -> None


    is_valid [1;2;3;4;5;6;9] (* int list option = Some [2; 3; 4; 5; 6] *)

Then with this function, you can use it with simple pattern matching to look for the middle of valid lists:

match is_valid l with
| Some middle -> middle (* the middle of the list *)
| None -> [] (* nothing — list was invalid *)