﻿ (* 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