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).