(* CS:4420 Artificial Intelligence Spring 2019 The University of Iowa Instructor: Cesare Tinelli *) (* OCaml examples seen in class *) (* Function declarations *) (* declaring a funtion in integer input and integer output *) let next (n : int) : int = n + 1 ;; (* identifier next is bound to a function from int to int *) next ;; next(4) ;; (* more common syntax for function application *) next 4 ;; (* incorrect syntax: read as ((next next) 4) *) next next 4 ;; (* correct syntax *) next (next 4) (* input and output types are inferred automatically *) let fnext n = n +. 1.0 (* Type constraints can be added as needed/preferred *) let double (x : string) = x ^ x let double x : float = x +. x let double (x : int) : int = x + x (* functions with unit return type *) let print_value n = Printf.printf "The value is %i" n ;; (* // this expression evaluates to () // but also has the side effect of printing // a message on the standard output stream *) print_value 4 ;; (* Recursive function declarations *) let rec fac n = if n = 0 then 1 else n * fac (n - 1) ;; fac 7 ;; (* Mutually recursive function declarations *) let rec even n = if n = 0 then true else odd (n - 1) and odd n = if n = 0 then false else even (n - 1) (* Let binding visibility *) let a = 5 let f x = a + x ;; f 1 ;; (* *new* constant with name a *) let a = 2 ;; (* new constant shadows old one *) a ;; (* however, the old one still exists and is unchanged f was defined using the old a, not the new one *) f 1 ;; (* declarations can be nested *) let h = (let z = 4 in z + z) ;; (* ^^^^^^^^^^^^^^^^^^ z is a local variable *) (* // The scope of a let binder is the rest of the file // unless explicitly reduced with 'in' *) let q = 3 in q + 1 ;; (* ^^^^^ scope of q *) (* q not defined here *) q ;; let x = 12 (* outer x is 12, its scope starts with this line *) (* and extends until the end of the file *) let r = let x = 3 in x * x ;; (* inner x is 3, its scope is only this line *) x ;; (* outer x unchanged *) (* nested declarations shadow less local ones *) let k = 1 in (let k = 2 in (let k = 3 in Printf.printf "%i" k ); (* within scope of each of three lets *) Printf.printf "%i" k (* within scope of first two lets only *) ); Printf.printf "%i" k (* within scope of first let only *) ;; (* function names can have local scope too *) let h x = x + 3 in h 1 (* scope of identifier h *) ;; h (* error *) (* // this implies that functions can be defined locally // within other functions *) let f x = let l_square x = x * x in let l_double x = x + x in l_square(x) - l_double(x) ;; f 5 ;; l_square 3 ;; (* error *) (* 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 the 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 mismatch *) (* _ is a special pattern variable that matches anything *) 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 *) sum (3,4) ;; (* sum contrast with this *two* argument function *) let sum2 x y = x + y ;; sum2 3 4 (* takes two arguments *) ;; (* deep patterns are possible *) let psum ((x1,x2), (y1,y2)) = (x1 + y1, x2 + y2) (* ^^^^^^^^^^^^^^^^^^ matches agains a pair of pairs of ints *) ;; 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 expression 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" ;; (* match is typically used to define functions by cases *) let conv n = match (n * n + 1) with (* matched term can be any term *) | 0 -> "zero" | 1 -> "one" | 2 -> "two" | _ -> "enough already" (* match expressions allow for more coincise and readable code *) (* Is is immediately clear what this function does? *) let bothZero x1 x2 = if x1 <> 0 then false else if x2 = 0 then true else false (* How about this one? *) let bothZero x y = match (x,y) with | (0,0) -> true | _ -> false (* 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_op 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 (* the scope of a pattern variable is the rule it occurs in *) 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 can lead to run-time errors *) match 4 with 1 -> "OK" | 2 -> "Also OK" ;; (* compiler will issue a warning with non-exhastive patterns *) let g x = match x with 1 -> "OK" | 2 -> "Also OK" ;; (* same shadowing rules apply for 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 (* Algebraic data types *) (* aka Discriminated Unions *) type mood = Happy | Sad | Mad let m = Happy let f x = match x with Happy -> "\n:-)\n" | Sad -> "\n:-(\n" | _ -> "\n:-@\n" ;; print_string (f m) ;; print_string (f Mad) ;;