Click here to Skip to main content
15,890,512 members
Articles / Programming Languages / F#

F#13 : Pattern Matching

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
1 May 2014CPOL7 min read 16.9K   2  
So last time we looked at Arrays, this time we will look at another core F# technique called pattern matching. Patterns are rules for transforming input data.

So last time we looked at Arrays, this time we will look at another core F# technique called pattern matching. Patterns are rules for transforming input data. They are used throughout the F# language to compare data with a logical structure or structures, decompose data into constituent parts, or extract information from data in various ways.

Pattern matching typically takes the form

match expression with
| pattern [ when condition ] -> result-expression

We will now proceed to look at pattern matching various things we have seen so far, however as before MSDN also has good coverage on pattern matching since it is such an integral part of using F#. You can read more at MSDN right here : http://msdn.microsoft.com/en-us/library/dd547125.aspx

Function Keyword

Just before we start I just want to walk through one example that you may see from time to time, that may confuse you. To understand it, we will be starting with some C# code. which is as follows:

public void EnumerableDemo()
{
    Enumerable.Range(1, 10).Select(x => DoubleIt(x));
}

public int DoubleIt(int input)
{
    return input*2;
}

This code is very simple, and it is really JUST the select projection that is of interest here. The code above is totally valid, where we use the Select extension method, and call the DoubleIt() method, where we pass in the int input value and get an int value returned. There is however another way (those of you that have Resharper installed will no doubt be well aware of this, as Resharper is very proactive about warning users of the alternative) of doing this, which is as follows:

public void EnumerableDemo()
{
    Enumerable.Range(1, 10).Select(DoubleIt);
}

public int DoubleIt(int input)
{
    return input*2;
}

See how we were able to refine the syntax of the select extension method call. In C# this is called a method group. The reason I went into the C# code above is that F# also allows you to perform the same sort of trick (although nothing to do with LINQ/Select methods) when we do pattern matching, lets see a trivial example shall we:

In this code we express the pattern matching using the full syntax (match x with….)

let someFunction x = 
    match x with
    | 10 -> printfn "it was 10"
    | _ -> printfn "it was not 10"



do someFunction 10
do someFunction 12

Which works as expected (you may have to trust me on this one)

We can however also do this instead:

let someFunction = function
    | 10 -> printfn "it was 10"
    | _ -> printfn "it was not 10"

do someFunction 10
do someFunction 12

Where we have removed the input parameter all together, and now use the “function” keyword.The “function” keyword works anywhere a function definition or lambda can be used

Order

When using pattern matching the order of the matches is important, for example consider this code (the last match is known as a wild card match, which we will talk about in just a minute):

let someFunction x = 
    match x with
    | 1 -> printfn "it was 1"
    | 2 -> printfn "it was 2"
    | _ -> printfn "it was something else"

do someFunction 1
do someFunction 2
do someFunction 3

Which when run, gives us what we want, as shown below:

image

But what happens if we put the wild card match first, so we had this code:

let someFunction x = 
    match x with
    | _ -> printfn "it was something else"
    | 1 -> printfn "it was 1"
    | 2 -> printfn "it was 2"

do someFunction 1
do someFunction 2
do someFunction 3

This time we get the following output which is obviously wrong, which is due to the matching hitting the wild card match first for any input value

image

Exhaustive Matches

We will stick with the example we have been seeing so far. The only amendment I have made is that I have removed the wild card match, so we are now left with this code:

let someFunction x = 
    match x with
    | 1 -> printfn "it was 1"
    | 2 -> printfn "it was 2"

do someFunction 1
do someFunction 2
do someFunction 3

This code looks totally fine, and compiles just fine. However when you try and run this code, you will get a MatchFailureException thrown, due to the unmatched 3 value.

image

This is easily fixed we can just include a new match for the 3, which would give us this new code:

let someFunction x = 
    match x with
    | 1 -> printfn "it was 1"
    | 2 -> printfn "it was 2"
    | 3 -> printfn "it was 3"

do someFunction 1
do someFunction 2
do someFunction 3

But then what happens when we need to match more than 1,2 or 3. Arghhh, Surely there is a better way. Luckily there is, it is called the “wild card match” which we will look at next.

Wild Card Pattern

The wild card match, will literally match anything, it is expressed using an underscore “_” and it can be used alone or as part of a composite pattern matching (such as maybe a tuple).

To keep things simple we will continue to work with our trivial example. So I have left the explicit matches in place for 1 and 2, but have also introduced a wild card match for anything else. So the revised code is as follows:

let someFunction x = 
    match x with
    | 1 -> printfn "it was 1"
    | 2 -> printfn "it was 2"
    | _ -> printfn "it was somthing else"

do someFunction 1
do someFunction 2
do someFunction 3
do someFunction 4
do someFunction 5

The wildcard match is great, but one area when you would want to use exhaustive matching over the wild card is when matching discriminated unions, I would suggest you DO NOT use a wild card match there.

Variable Pattern

The variable pattern allow you to assign the values of the match to variable name, which you may use to the right of the -> symbol. Here is an example:

let someFunction x =
    match x with
    | (var1, var2) when var1 > var2 -> printfn "%d is greater than %d" var1 var2 
    | (var1, var2) when var1 < var2 -> printfn "%d is less than %d" var1 var2
    | (var1, var2) -> printfn "%d equals %d" var1 var2

someFunction (1,2)
someFunction (2, 1)
someFunction (0, 0)

Which when run produces this:

image

When working with tuples this is VERY useful pattern, as it allows you to match on the different parts of the tuple

And Pattern

You can also use the AND operator “&” when pattern matching that would allow you to match one or more conditions. Here is a very simple example based on matching one or more int values contained within a tuple that is supplied to the function (I have borrowed this example from MSDN)

let detectZeroAND point =
    match point with
    | (0, 0) -> printfn "Both values zero."
    | (var1, var2) & (0, _) -> printfn "First value is 0 in (%d, %d)" var1 var2
    | (var1, var2)  & (_, 0) -> printfn "Second value is 0 in (%d, %d)" var1 var2
    | _ -> printfn "Both nonzero."

detectZeroAND (0, 0)
detectZeroAND (1, 0)
detectZeroAND (0, 10)
detectZeroAND (10, 15)

Which when run produces the following:

image

Or Pattern

You can also use the OR operator “|” when pattern matching that would allow you to match one or more conditions. Here is a very simple example based on matching one or more int values.

let orFunction x =
    match x with 
    // OR pattern.
    | 0 | 1 -> printfn "It was 0 or 1" 
    | 2 -> printfn "It was 2"

for x in 0..2 do orFunction x

Which when run produces this:

image

You can also apply this to things like Tuples, here is an example of that:

let detectZeroOR point =
    match point with
    | (0, 0) | (0, _) | (_, 0) -> printfn "Zero found."
    | _ -> printfn "Both nonzero."

detectZeroOR (0, 0)
detectZeroOR (1, 0)
detectZeroOR (0, 10)
detectZeroOR (10, 15)

Which when run produces this:

image

Matching Constants

It is sometimes useful to keep certain constant values that hold special meaning, such as perhaps an int value who’s real intention is “FAILURE”, and it may be nice to pattern match again these constants. Here is an example of you you may do that, take not of the [<Literal>] attribute that we applied to the int values, such that they could be treated as literal values in pattern matching and other places.

[<Literal>]
let FAILURE = 0
  
[<Literal>]
let UNKNOWN = 1
  
[<Literal>]
let SUCCESS = 2
  
let literalFunction x =
    match x with 
    // The following line contains literal patterns combined with an OR pattern.
    | FAILURE | UNKNOWN -> printfn "It did not work"
    // The following line contains a literal pattern.
    | SUCCESS -> printfn "It worked\r\n"

  for x in 0..2 do literalFunction x

When run this produces this:

image

Matching Tuples

Matching tuples is an interesting one, as you can use the wild card for different parts of the tuple matching, or even the whole tuple. Here is example

let someFunction x = 
    match x with
    | (1,1) -> printfn "it was (1,1)"
    | (_,2)-> printfn "2nd part was 2"
    | (3,_) -> printfn "1st part was 3"
    | (_,_) -> printfn "its a freak"

do someFunction (1,1)
do someFunction (4,2)
do someFunction (3,5)
do someFunction (10,4)

Which when run produces the following results

image

Matching Options

Matching options is also very easy we just do the following sort of thing:

let someFunction x = 
    match x with
    | Some(y) -> printfn "Some %A" y
    | None-> printfn "None"

do someFunction (Option.Some(24))
do someFunction None

Which when run produces the following results

image

Matching Records

Matching records is also pretty simply to do, where you can match the individual field level parts of the record. For example here is some code that will match on the Name field of a Person type. You could of course loop in any of the record types fields in the pattern matching

type Person = { Name : string }
.....
.....
let someFunction x = 
    match x with
    | { Name= "Fred"} -> printfn "Its Fred" 
    | { Name= "Sam"} -> printfn "Its Sam" 
    | _ -> printfn "Its not Fred or Sam" 

do someFunction { Name = "Sam"}
do someFunction { Name = "Fred"}
do someFunction { Name = "Daniel"}

Which when run will give the following results

image

Matching Discriminated Unions : Empty cases

There are 2 flavours of unions that we can match against, the empty union cases (much like enums in .NET),which we can match as follows:

type SupportedAnimals = 
    | Cat 
    | Dog 
    | Spider 
    | Snake
.......
.......
let someFunction x = 
    match x with
    | Cat -> printfn "Its a Cat" 
    | Dog -> printfn "Its a Dog" 
    | Spider -> printfn "Its a Spider" 
    | Snake -> printfn "Its a Snake" 

do someFunction SupportedAnimals.Cat
do someFunction SupportedAnimals.Dog

Which when run will give the following results

image

Matching Discriminated Unions

Whilst the more complex union cases require a bit more work, but it is still quite simple to do, here is an example

type SmallArmyRank = 
    | General of int
    | Private
    | Seargent
    | Colonel
....
....
....
....
let someFunction x = 
    match x with
    | General 1 -> printfn "Its 1 star General" 
    | General 2 -> printfn "Its 2 star General"  
    | General y -> printfn "Its %A top brass General"  y
    | Private -> printfn "Its Private"

do someFunction (SmallArmyRank.General(1))
do someFunction (SmallArmyRank.General(2))
do someFunction (SmallArmyRank.General(4))
do someFunction SmallArmyRank.Private

Which looks like this when run:

image

Matching Lists

It is also possible to match the different parts of lists, such as head and tail, where you can use the cons operator “::” within the pattern matching. Here is an example:

let theList = [ 1; 2; 3; 4 ]

let someFunction l =
    match l with
    | head :: tail -> printf "The head of the list is '%A' and the tail is '%A'\r\n " head tail
    | [] -> printfn "The list is empty\r\n"


do someFunction theList
do someFunction List.Empty

Which when run produces the following results:

image

Though I would say more typically you would use some sort of recursive function when dealing with lists, Here is a small demo of how to use recursion and pattern matching for lists

let theList = [ 1; 2; 3; 4 ]

let rec someFunction l =
    match l with
    | head :: tail -> 
        printf "The head is '%A'\r\n" head
        someFunction tail
    | [] -> printfn "The list is empty\r\n"

do someFunction theList

Which when run gives the following results:

image

You can also match the individual elements of a list, which is known as the “list pattern”, this only works if you know the exact number of elements in the list. Here is a demo of it in action.

let theList3 = [ 1; 2; 3 ]
let theList5 = [ 1; 2; 3; 4; 5; ]

let someFunction list =
    match list with
    | [] -> 0
    | [ _ ] -> 1
    | [ _; _ ] -> 2
    | [ _; _; _ ] -> 3
    | _ -> List.length list


printfn "The list [ 1; 2; 3 ] has a length of %d" (someFunction theList3)
printfn "The list [ 1; 2; 3; 4; 5; ] has a length of %d" (someFunction theList5)

Which when run gives the following results:

image

Matching Arrays

It is pretty simple to match against arrays too, I should point out though I am crazy I am not crazy enough to delve into pattern matching 2D/3D and 4D arrays, that is up to you. Here is an example matching a 1D array:

let someFunction x =
    match x with
    | [|var|] -> printfn "Its 1D array with 1 element"
    | [|var1;var2|] -> printfn "Its 1D array with 2 elements"
    | _ -> printfn "you are on your own buddy"

do someFunction (Array.init 1 (fun a -> 0))
do someFunction (Array.init 2 (fun a -> 0))
do someFunction (Array.init 4 (fun a -> 0))

Which when run gives the following results:

image

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
-- There are no messages in this forum --