Using Boolean Functions as Pattern Discriminators in F #

I tried using this game, but I could not find a set of words that told me what I was trying to do.

I am trying to solve Project Euler Problem 54 , and I have this rather ridiculous function:

let evaluate hand = if isRoyalFlush hand then 9 elif isStraightFlush hand then 8 elif isFour hand then 7 elif isFullHouse hand then 6 elif isFlush hand then 5 elif isStraight hand then 4 elif isThree hand then 3 elif isTwoPair hand then 2 elif isPair hand then 1 else 0 

All isSomething keywords are functions that take a string array and return a boolean. Is there a more elegant way to do this using pattern matching?

This does not work:

 match true with | isRoyalFlush hand -> 9 | isStraightFlush hand -> 8 // .. etc 

I am looking for something like this:

 match hand with | isRoyalFlush -> 9 | isStraightFlush -> 8 // .. etc 

I remember something like this at some point, but I canโ€™t remember where and how to find it.

+7
pattern-matching f # active-pattern
source share
2 answers

You want active templates :

 let (|IsRoyalFlush|_|) hand = if isRoyalFlush hand then Some () else None let (|IsStraightFlush|_|) hand = if isStraightFlush hand then Some() else None // etc. match hand with | IsRoyalFlush -> 9 | IsStraightFlush -> 8 // etc. 

Or better, pull all of this generic code into a simple active template constructor:

 let toActivePattern pred x = if pred x then Some () else None let (|IsRoyalFlush|_|) = isRoyalFlush |> toActivePattern let (|IsStraightFlush|_|) = isStraightFlush |> toActivePattern // etc. match hand with | IsRoyalFlush -> 9 | IsStraightFlush -> 8 // etc. 

If you donโ€™t understand how this second example works, leave a comment and I go deeper.

Drawing up active patterns together

Since active templates are just functions, you can use the standard composition of functions to combine them. Well, almost a standard functional composition. A normal functional composition with the >> operator means "take the result of function 1 and use it as an input to function 2". But here function 1 and function 2 return Some () , but accept int; you cannot pass pin 1 to input 2 because they are not compatible. But we want to actually pass the same input to both functions and combine their output.

Therefore, instead of using the normal composition of functions, we define our own function, which takes two active templates and returns Some () if both templates correspond to the input:

 let matchesBoth pattern1 pattern2 x = match pattern1 x with | None -> None | Some _ -> pattern2 x 

And while we're on it, let's define a custom operator so you can see how it works. This matchesBoth function matchesBoth very similar to the && operator, because it will return Some () only if both patterns return Some () for any given input x . We do not have to overload the && operator to select a different type, so we will create a custom operator that looks like && but reminds us that it combines two active patterns. If our operator looks like |&&| It should be perfect. So let's create it:

 let (|&&|) = matchesBoth 

What is it! Now we can do something like:

 let (|Div3|_|) n = if n % 3 = 0 then Some () else None let (|Div5|_|) n = if n % 5 = 0 then Some () else None let (|Div15|_|) = (|Div3|_|) |&&| (|Div5|_|) let fizzbuzz n = match n with | Div15 -> "FizzBuzz" | Div5 -> "Buzz" | Div3 -> "Fizz" | _ -> string n fizzbuzz 30 // Result: "FizzBuzz" fizzbuzz 31 // Result: "31" 

Or for your example:

 let (|IsStraightFlush|_|) = (|IsStraight|_|) |&&| (|IsFlush|_|) 
+8
source share

Active templates are certainly an alternative, but I sometimes use a table of functions and values:

 let handScores = [| //Which hand? Score isRoyalFlush , 9 isStraightFlush , 8 isFour , 7 isFullHouse , 6 isFlush , 5 isStraight , 4 isThree , 3 isTwoPair , 2 isPair , 1 (fun _ -> true) , 0 // Matches all hands |] let handScore hand = handScores |> Array.pick (fun (t, s) -> if t hand then Some s else None) 
+7
source share

All Articles