Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to match multiple copies of a value?

F#'s pattern matching is very powerful so it felt natural to write:

match (tuple1, tuple2) with
| ((a, a), (a, a)) -> "all values are the same"
| ((a, b), (a, b)) -> "tuples are the same"
| ((a, b), (a, c)) -> "first values are the same"
// etc

However, the first pattern match gives a compiler error:

'a' is bound twice in this pattern

Is there a cleaner way to do it than the following?

match (tuple1, tuple2) with
| ((a, b), (c, d)) when a = b && b = c && c = d -> "all values are the same"
| ((a, b), (c, d)) when a = c && b = d -> "tuples are the same"
| ((a, b), (c, d)) when a = c -> "first values are the same"
// etc
like image 491
Mark Pattison Avatar asked Jan 28 '13 11:01

Mark Pattison


People also ask

How do I get multiple matches in VLOOKUP?

To lookup multiple matches with the VLOOKUP Function you need to create a helper column within the table of data. The helper column uses the COUNTIF Function to create a unique ID for each instance. The helper column must be the leftmost column within the data set.

Can index match Add multiple results?

You can insert as many numbers as you want, apart from the number1 all are optional. Your provided numbers can be within a range, in practical use, more often you need to provide a range of numbers inside the function. Here we will provide the numbers as a range, and the INDEX-MATCH will do the trick for us.


2 Answers

This is a perfect use case for F#'s "active patterns". You can define a couple of them like this:

let (|Same|_|) (a, b) =
    if a = b then Some a else None

let (|FstEqual|_|) ((a, _), (c, _)) =
    if a = c then Some a else None

And then clean up your pattern matching with them; note how the first case (where all values are equal) uses the nested Same pattern to check that the first and second elements of the tuple are equal:

match tuple1, tuple2 with
| Same (Same x) ->
    "all values are the same"
| Same (x, y) ->
    "tuples are the same"
| FstEqual a ->
    "first values are the same"
| _ ->
    failwith "TODO"

Performance tip: I like to mark simple active patterns like these with inline -- since the logic within the active patterns is simple (just a few IL instructions), it makes sense to inline them and avoid the overhead of a function call.

like image 96
Jack P. Avatar answered Sep 21 '22 05:09

Jack P.


You can use parameterized active patterns to remedy the issue.

let (|TuplePairPattern|_|) ((p1, p2), (p3, p4)) ((a, b), (c, d)) =
    let matched =
        [(p1, a); (p2, b); (p3, c); (p4, d)]
        |> Seq.groupBy fst
        |> Seq.map (snd >> Set.ofSeq)
        |> Seq.forall (fun s -> Set.count s = 1)
    if matched then Some () else None

Particularly, you should define a pattern in a form of literals (chars, strings, etc).

match tuple1, tuple2 with
| TuplePairPattern(('a', 'a'), ('a', 'a')) -> "all values are the same"
| TuplePairPattern(('a', 'b'), ('a', 'b')) -> "tuples are the same"
| TuplePairPattern(("a", "b"), ("a", "c")) -> "first values are the same"
// etc
like image 26
pad Avatar answered Sep 20 '22 05:09

pad