You can define an active pattern that is parameterized by a pattern that you want to detect. In your example, you write these patterns using a pseudo-syntax with array containing either a
or not a
, but you could quite easily write them as actual arrays that contain the same value when you want to have matching values in the input. For example, something like:
[| 'a'; 'a'; 'b'; 'a'; 'a'; |]
It could even be a string as in @bytebuster's solution (you can convert string
to char[]
using the ToCharArray
method). Then you can define active pattern:
let inline (|ArrayPattern|_|) pattern input =
let matches =
Array.zip pattern input
|> Seq.groupBy fst
|> Seq.forall (fun (_, values) ->
let cases = values |> Seq.map snd |> set
Set.count (cases - Set.singleton 0) <= 1)
if matches then Some() else None
I guess this is essentially the same as what @bytebuster implements. Here, I group the elements of the input array by their corresponding key in the pattern and then use set operations to check that there is at most one non-zero value for each group.
To use it, you can now match an int[]
against ArrayPattern [| ... |]
where the array parameter specifies the specific pattern you're looking for:
match [| 1; 1; 2; 0; 1 |] with
| ArrayPattern [| 'a'; 'a'; 'b'; 'a'; 'a'; |] -> printfn "match"
| _ -> printfn "not"