(* CS:3820, Fall 2018 *)
(* Cesare Tinelli *)
(* The University of Iowa *)
(* F# examples seen in class *)
(* Pattern matching *)
let t = (3,5,2)
// left-hand sides of let can be a pattern with variables
// declares new variables x1, x2, x3 and assigns them value 3, 5, 2, respectively
let (x1, x2, x3) = t
// extracts only first two components of t, into y1 and y2 respectively
let (y1, y2, _) = t
let (y1, y2) = t // error, pattern/type mismacth
let t = ((2,3), 5, (6,2))
// deep pattern matching: extracts first element of third element of t
let (_, _, (x,_)) = t
// patterns can also occur in argument positions of function definitions
let sum (x,y) = x + y
// ^^^^^ tuple pattern!
// sum is a *unary* function, taking a 2-element tuple of integers as argument
let p1 = (3, 4)
sum p1
// contrast sum with this *two* argument function
let sum2 x y = x + y
sum2 (2, 1) // type error, sum2 takes two arguments
// nested patterns are possible
let psum ((x1,x2), (y1,y2)) =
// ^^^^^^^^^^^^^^^^^^ nested pattern
(x1 + y1, x2 + y2)
psum ((1,2), (3,4))
// patterns are extremely useful with the match construct:
//
// match T with
// P1 -> T1
// | P2 -> T2
// ...
// | Pn -> Tn
//
// where T, T1, ..., Tn are terms and P1, ..., Pn are patterns.
// All the Ti's must have the same type.
// Term T1 is matched against each pattern Pi in sequence,
// stopping with the first one that matches.
// The value of the whole match is the value of the corresponding Ti
// The scope of any variable in patter Pi is just term Ti
// Each Pi -> Ti is called a _rule_
let n = 1
match n with
0 -> "zero" // pattern matches only 0
| 1 -> "one" // pattern matches only 1
| _ -> "other" // pattern matches any number
// since match expressions are indeed expressions
// they can occur in any expression position.
"the value of n is " + match n with
0 -> "zero"
| 1 -> "one"
| _ -> "other"
3 = true
let conv n = // matched term can be any term
match (n * n + 1) with // matching is done on the term's value
0 -> "zero"
| 1 -> "one"
| 2 -> "two"
| _ -> "enough already"
// match expressions allow for more coincise and readable code
// Is it immediately clear what this function does?
let bz x1 x2 =
if x1 <> 0 then
false
else if x2 = 0 then
true
else
false
// How about this one?
let bz2 x y =
match (x,y) with
| (0,0) -> true
| _ -> false
// Example: defining the xor operator
let xor x y =
match (x,y) with
(false, false) -> false
| (false, true) -> true
| (true, false) -> true
| (true, true) -> false
// using patterns to merge cases
let xor x y =
match (x,y) with
(false, true) -> true
| (true, false) -> true
| _ -> false
// the input here is a pair that can be pattern matched directly
let and p =
match p with
// returns the second component of p if the first component is true
(true, y) -> y
// returns false if the first component of p is false
| (false, _) -> false
// pattern variables are defined only locally in each match rule
let f p =
match p with
(true, y) -> y
| (false, _) -> not y // error: y is undefined here
// a pattern consisting of just a variable matches anything
match 4 with
x -> x + 1
| y -> y + 2 // unreachable case!
// non-exhastive patterns lead to run-time errors
match 4 with
1 -> "OK"
| 2 -> "Also OK"
// compiler will often issue a warning with non-exhastive patterns
let g x =
match x with
1 -> "OK"
| 2 -> "Also OK"
// same shadowing rules apply to pattern variables too
let f v1 v2 =
match (v1, v2) with
(5, v1) -> v1 // local v1 shadows input v1,
| _ -> v1 // this is the input v1
f 5 6
f 4 6
// match is also helpful in writing more readable recursive functions
let rec fact n =
if n = 0 then
1
else
n * (fact (n - 1))
let rec fact n =
match n with
0 -> 1
| _ -> n * (fact (n - 1))
// alternative syntax for function declarations with pattern matching
// the argument variable is implicit and matching is done on its value
let rec fact = function
0 -> 1
| n -> n * (fact (n - 1))
let bothZero = function
(0,0) -> true
| _ -> false