(* CIS341 Fall 2008 *) (* Some example OCaml code *) (* Comments begin with "(*" and end with "*)". Obviously, they nest *) (* Bind a value to a variable *) let pi : float = 3.14159 (* Define a function *) let square : float -> float = fun (x:float) -> x *. x let area : float -> float = fun (r:float) -> pi *. square r (* Define a recursive function *) let rec fact : int -> int = fun (n:int) -> if (n <= 0) then 1 else n * fact (n-1) (* You can [usually] leave off the types... ML will figure them out. *) let pi = 3.14159 (* It's more convenient to define functions with "let" *) let square x = x *. x let area r = pi *. square r let rec fact n = if n <= 0 then 1 else n * fact (n-1) (* Note: For debugging, it is very helpful to put type *) (* annotations into your program, especially for top-level *) (* functions. This will help you understand your code *) (* better and help the compiler produce better error *) (* messages. *) (* local declarations *) let area(r:float):float = let square x = x *. x in pi *. square r (* Another way to write factorial -- using a loop *) let fact'(n:int):int = let rec loop (c:int) (a:int):int = if c <= 0 then a else loop (c-1) (c*a) in loop n 1 (* Note that loop takes two arguments: c and a *) (* It has type int -> int -> int *) (* This is called "curried" form. *) let rec loop (c:int) (a:int):int = if c <= 0 then a else loop (c-1) (c*a) (* OCaml also supports tuples *) let twople = (3, "hello") let threeple = (3, "hello", 2.0) (* We could have written factorial like this: *) (* This is called "uncurried" form. *) let fact''(n:int):int = let rec loop (c,a) = if c <= 0 then a else loop (c-1, c*a) in loop(n,1) (* In general, OCaml's libraries use curried style rather *) (* than uncurried style function. *) (***************************************************) (* OCaml allows users to define new data types. *) (* Define a new inductive type 'nat' that is either Zero or the * successor of a previous 'nat' *) type nat = Zero | Succ of nat (* Some example nats *) let two = Succ(Succ(Zero)) let four = Succ(Succ(two)) (* Define a new datatype corresponding to booleans: *) type boolean = True | False (* Ocaml has a built-in type of booleans defined: * * type bool = true | false *) (* These datatypes can be deconstructed using pattern matching *) (* Pattern matching against datatypes *) let not(x:boolean):boolean = match x with | True -> False | False -> True (* Note that patterns can bind variables to subcomponents of the *) (* datatype. *) (* Map a nat to an ML integer *) let rec nat2int(n:nat):int = match n with | Zero -> 0 | Succ(m:nat) -> 1 + nat2int m (* The '_' wildcard pattern matches any value. *) let isZero(n:nat):boolean = match n with | Zero -> True | Succ _ -> False (* Add two nats *) let rec add (n:nat) (m:nat):nat = match n with | Zero -> m | Succ(n':nat) -> add n' (Succ m) (* Patterns can appear in many places: *) (* - inside the cases of 'match' expressions. *) (* - in 'let' expressions 'let = e1 in e2 *) (* - as function arguments *) (* NOTE: A simple variable like 'x' in let x = e is *) (* a pattern! *) (* Example: Tuples *) let rec add' ((n:nat), (m:nat)):nat = match n with | Zero -> m | Succ(n':nat) -> add n' (Succ m) let rec add'' (p:nat * nat):nat = match p with | ((n:nat), (m:nat)) -> match n with | Zero -> m | Succ(n':nat) -> add'' (n', (Succ m)) (* Moral -- every function in ML takes 1 argument and returns 1 result. If you want to use more than one argument or one result, then you can use a tuple. *) (***************************************************************) (* A slightly more complex datatype *) (* An intlist is either Nil or a Cons(i,x) where i is an int and x is an intlist. Note the similarity with nat. *) type intlist = Nil | Cons of int * intlist let list3 : intlist = Cons(3,Cons(2,Cons(1,Nil))) let list5 : intlist = Cons(5,Cons(4,list3)) let rec merge (x:intlist) (y:intlist):intlist = match x,y with | Nil,_ -> y | _,Nil -> x | Cons(i,x'), Cons(j,y') -> if i < j then Cons(i,merge x' y) else Cons(j,merge x y') let rec split (x:intlist) (y:intlist) (z:intlist):(intlist * intlist) = match x with | Nil -> (y,z) | Cons(i,x') -> split x' z (Cons(i,y)) let rec mergesort (x:intlist):intlist = match x with | Nil -> Nil | Cons(_,Nil) -> x | _ -> let (a,b) = split x Nil Nil in merge (mergesort a) (mergesort b) (* Ocaml has built in generic (or "polymorphic") lists *) (* that look like: *) (* type 'a list = [] | :: of 'a * ('a list) *) (* where "[]" is a null-ary constructor *) (* "::" is an infix identifier pronounced "cons" *) (* "'a" represents a type variable *) let list3 : int list = 3::2::1::[] let list5 : int list = 5::4::list3 (* Bracket notation for lists: *) let list3 : int list = [3;2;1] let list5 : int list = [5;4;3;2;1] let listb : bool list = true::false::[] let listb : bool list = [true;false] let names : string list = ["Steve"; "Amir"; "Sue"; "Milo"] let fullnames : (string list) list = [["Steve"; "Zdancewic"]; ["Amir"; "Roth"]; ["Sue"; "Davidson"]; ["Milo"; "Martin"]]; (* Datatypes can be used to make "heterogeneous" lists: *) type int_or_string = Int of int | String of string let mixed = [Int 42; String "Steve"; Int 12; String "Amir"; String "Milo"] (* Working with lists *) (* Sum the elements of an int list *) let sum (x:int list) : int = let rec loop (x:int list) (accum:int) = match x with | [] -> accum | i::rest -> loop rest (accum + i) in loop x 0 (* Multiply the elements of an int list *) let prod (x:int list) : int = let rec loop (x:int list) (accum:int) = match x with | [] -> accum | i::rest -> loop rest (accum * i) in loop x 1 (* factor out the common parts *) let foldl (combine : int -> int -> int) (zero:int) (x:int list) : int = let rec loop (x:int list) (accum:int) = match x with | [] -> accum | i::rest -> loop rest (combine accum i) in loop x zero let sum = foldl (+) 0 let prod = foldl ( * ) 1 (* even better... make it type generic, i.e. polymorphic *) let foldl (combine : 'b -> 'a -> 'b) (zero:'b) (x:'a list) : 'b = let rec loop (x: 'a list) (accum:'b) = match x with | [] -> accum | i::rest -> loop rest (combine accum i) in loop x zero let sum = foldl (+) 0 let prod = foldl ( * ) 1 let concat = foldl (fun x -> fun y -> x ^ y) "" let snoc = fun x -> fun y -> y::x (* reverse a list *) let rev : 'a list -> 'a list = fun x -> foldl snoc [] x (* reverse x onto y *) let revappend x y = foldl snoc y x let append x y = revappend (rev x) y (* map a function f over a list, producing the results *) (* in reverse order *) let map_rev (f:'a -> 'b) : 'a list -> 'b list = foldl (fun tl -> fun hd -> (f hd)::tl) [] (* map a function over a list *) let map f x = rev (map_rev f x) (******************************) (* records *) type ratio = {num: int; denom: int} let half = {num = 1; denom = 2} (* to access components of a record, use "." *) let add_ratio r1 r2 = {num = r1.num * r2.denom + r2.num * r1.denom; denom = r1.denom * r2.denom} (*******************************) (* mutable features *) (* r is a mutable reference with contents 0 *) let r : int ref = ref 0 (* to read out the contents use ! *) let rval : int = !r (* to update the contents, use := *) let x : unit = r := 42 (* a counter "object" *) type counter = { read : unit -> int; inc : unit -> unit } (* implement counters using integers *) let int_counter () = let c = ref 0 in let r () = !c in let i () = c := r() + 1 in {read = r; inc = i} (* Note there is a type error in the following code *) (* implement using the type Nat from above *) (* let nat_counter () = let c = ref Zero in let r () = nat2int (!c) in let i () = c := Succ(r()) in {read = r; inc = i} *)