F# Tutorial on F# Pattern Matching

pattern matching allows you to “compare data with a logical structure or structures, decompose data into constituent parts, or extract information from data in various ways”.

in other terms, it provides a more flexible and powerful way of testing data against a series of conditions and performing some computations based on the condition met.

conceptually, it is like a series of if… then statements.

syntax

in high level terms, pattern matching follows this syntax in f# −

match expr with
| pat1 - result1
| pat2 -> result2
| pat3 when expr2 -> result3
| _ -> defaultresult

where,

  • each | symbol defines a condition.
  • the -> symbol means "if the condition is true, return this value...".
  • the _ symbol provides the default pattern, meaning that it matches all other things like a wildcard.

example 1

the following example, calculates the fibonacci numbers using pattern matching syntax −

let rec fib n =
   match n with
   | 0 -> 0
   | 1 -> 1
   | _ -> fib (n - 1) + fib (n - 2)
for i = 1 to 10 do
   printfn "fibonacci %d: %d" i (fib i)

when you compile and execute the program, it yields the following output −

fibonacci 1: 1
fibonacci 2: 1
fibonacci 3: 2
fibonacci 4: 3
fibonacci 5: 5
fibonacci 6: 8
fibonacci 7: 13
fibonacci 8: 21
fibonacci 9: 34
fibonacci 10: 55

you can also chain together multiple conditions, which return the same value. for example −

example 2

let printseason month =
   match month with
   | "december" | "january" | "february" -> printfn "winter"
   | "march" | "april" -> printfn "spring"
   | "may" | "june" -> printfn "summer"
   | "july" | "august" -> printfn "rainy"
   | "september" | "october" | "november" -> printfn "autumn"
   | _ -> printfn "season depends on month!"

printseason "february"
printseason "april"
printseason "november"
printseason "july"

when you compile and execute the program, it yields the following output −

winter
spring
autumn
rainy

pattern matching functions

f# allows you to write pattern matching functions using the function keyword −

let getrate = function
   | "potato" -> 10.00
   | "brinjal" -> 20.50
   | "cauliflower" -> 21.00
   | "cabbage" -> 8.75
   | "carrot" -> 15.00
   | _ -> nan (* nan is a special value meaning "not a number" *)

printfn "%g"(getrate "potato")
printfn "%g"(getrate "brinjal")
printfn "%g"(getrate "cauliflower")
printfn "%g"(getrate "cabbage")
printfn "%g"(getrate "carrot")

when you compile and execute the program, it yields the following output −

10
20.5
21
8.75
15

adding filters or guards to patterns

you can add filters, or guards, to patterns using the when keyword.

example 1

let sign = function
   | 0 -> 0
   | x when x < 0 -> -1
   | x when x > 0 -> 1

printfn "%d" (sign -20)
printfn "%d" (sign 20)
printfn "%d" (sign 0)

when you compile and execute the program, it yields the following output −

-1
1
0

example 2

let compareint 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

compareint (11,25)
compareint (72, 10)
compareint (0, 0)

when you compile and execute the program, it yields the following output −

11 is less than 25
72 is greater than 10
0 equals 0

pattern matching with tuples

the following example demonstrates the pattern matching with tuples −

let greeting (name, subject) =
   match (name, subject) with
   | ("zara", _) -> "hello, zara"
   | (name, "english") -> "hello, " + name + " from the department of english"
   | (name, _) when subject.startswith("comp") -> "hello, " + name + " from the department of computer sc."
   | (_, "accounts and finance") -> "welcome to the department of accounts and finance!"
   | _ -> "you are not registered into the system"

printfn "%s" (greeting ("zara", "english"))
printfn "%s" (greeting ("raman", "computer science"))
printfn "%s" (greeting ("ravi", "mathematics"))

when you compile and execute the program, it yields the following output −

hello, zara
hello, raman from the department of computer sc.
you are not registered into the system

pattern matching with records

the following example demonstrates pattern matching with records −

type point = { x: float; y: float }
let evaluatepoint (point: point) =
   match point with
   | { x = 0.0; y = 0.0 } -> printfn "point is at the origin."
   | { x = xval; y = 0.0 } -> printfn "point is on the x-axis. value is %f." xval
   | { x = 0.0; y = yval } -> printfn "point is on the y-axis. value is %f." yval
   | { x = xval; y = yval } -> printfn "point is at (%f, %f)." xval yval

evaluatepoint { x = 0.0; y = 0.0 }
evaluatepoint { x = 10.0; y = 0.0 }
evaluatepoint { x = 0.0; y = 10.0 }
evaluatepoint { x = 10.0; y = 10.0 }

when you compile and execute the program, it yields the following output −

point is at the origin.
point is on the x-axis. value is 10.000000.
point is on the y-axis. value is 10.000000.
point is at (10.000000, 10.000000).