310 likes | 449 Views
Objective Caml (Ocaml). Aaron Bloomfield CS 415 Fall 2005. ML history. ML first developed in late 1970’s Stands for Meta Langauage Not a “pure” function language Has functions with side effects, imperative programming capabilities Haskell is a pure functional language
E N D
Objective Caml (Ocaml) Aaron Bloomfield CS 415 Fall 2005
ML history • ML first developed in late 1970’s • Stands for Meta Langauage • Not a “pure” function language • Has functions with side effects, imperative programming capabilities • Haskell is a pure functional language • Uses Hindley-Milner type inference algorithm • Standard ML (SML) developed in 1990 • Most common version is njsml from Bell Labs • Caml developed in 1987 • Categorical Abstract Machine ML or CAML-ML or CAML • Ocaml came along in 1996 • Adds objects (which we won’t be using)
Invoking Ocaml • From Cygwin: Aaron@Eridanus ~ $ ocaml Objective Caml version 3.08.1 # 5;; - : int = 5 # Ctrl-D Aaron@Eridanus ~ $ • Can install Ocaml on Linux as well
Defining variables • All variables (and functions) are defined with the let command # let a = 5;; val a : int = 5 # let b = 5.5;; val b : float = 5.5 # let c = '5';; val c : char = '5' # let d = "5";; val d : string = "5" # • Ocaml automatically determines the type of the identifier from the definition • It does this for functions as well – more on this later
Operators • The standard operators with the standard precedences • Note that: • * is integer multiplication • *. is floating point multiplication • Same for the other operators (/ + -) • Thus, 4.5 - 3.2 won’t work • Use 4.5 -. 3.2 • And Ocaml won’t allow you to mix types • You have to convert from one type to another yourself
Lists • The main ‘workhorse’ in Ocaml # [1;2;3];; - : int list = [1; 2; 3] # let e = ["a";"b";"c"];; val e : string list = ["a"; "b"; "c"] # let f = [1,2,3];; val f : (int * int * int) list = [(1, 2, 3)] # let g = [1;'a';"b";2.0];; This expression has type char but is here used with type int # • You don’t have to declare a variable to declare a list • A comma separates parts of a tuple, not parts of a list • Lists must be homogenous – all parts of the same type
; vs , • ; separates elements of a list • , separates elements of a tuple # let g = [1;2;3];; val g : int list = [1; 2; 3] # let h = [1,2,3];; val h : (int * int * int) list = [(1, 2, 3)] # let i = [1,2;3,4];; val i : (int * int) list = [(1, 2); (3, 4)] # let j = [(1,2);(3,4)];; val j : (int * int) list = [(1, 2); (3, 4)] # • Parenthesis are just for human convenience
List operators • List.hd • Calls the hd (head) function from the List module • Returns the first element of a list • Raises an exception on an empty list • List.tl • Returns the list without the head element • Raises an exception on an empty list • :: • Takes a head element and a list and glues them together • @ • Takes two lists and glues them together
List operators • Examples # let l = [1;2;3];; val l : int list = [1; 2; 3] # List.hd l;; - : int = 1 # List.tl l;; - : int list = [2; 3] # 0 :: l;; - : int list = [0; 1; 2; 3] # [0] @ l;; - : int list = [0; 1; 2; 3] # • Note that :: takes an element and a list • Note that @ takes two lists
More complex types • From the HW: let dfa1 = ("0", [("0", "0", "2"); ("0", "1", "1"); ("1", "0", "3"); ("1", "1", "0"); ("2", "0", "0"); ("2", "1", "3"); ("3", "0", "1"); ("3", "1", "2")], ["0"]);; • The type is: val dfa1 : string * (string * string * string) list * string list
Defining functions # let f x = x+5;; val f : int -> int = <fun> # let g x = x+6;; val g : int -> int = <fun> # f 1;; - : int = 6 # f(1);; - : int = 6 # f(g(7));; - : int = 18 # • Ocaml automatically determines the type of the function • Called type inference
Functions with multiple parameters • A function that takes two int parameters is different than a function that takes a int * int tuple as a parameter # let h x y = x+y;; val h : int -> int -> int = <fun> # let i x,y = x+y;; Syntax error # let i (x,y) = x+y;; val i : int * int -> int = <fun> # h 1;; - : int -> int = <fun> # let j = h 1;; val j : int -> int = <fun> # j 2;; - : int = 3 # • Note the required use of parenthesis when the parameter is a tuple • A function that takes two parameters can have just one passed to it • This then returns another function
if … then syntax • Consider: # let x = 3;; val x : int = 3 # if x = 3 then 3;; This expression has type int but is here used with type unit # if x = 3 then 3 else 4;; - : int = 3 # • Note that parenthesis are not needed for the if condition • There must be an else clause
A bad function let rec foo x = if ( x < 0 ) then 1 else 2.0;; • Ocaml’s type inference can’t determine the return type (int or float?)
Defining recursive functions • Consider the Fibonacci function: # let fib n = if n=1 or n=2 then 1 else fib(n-1) + fib(n-2);; Unbound value fib • As fib has not been defined yet, Ocaml doesn’t know what the fib identifier means in the recursive call # let rec fib n = if n=1 or n=2 then 1 else fib(n-1) + fib(n-2);; val fib : int -> int = <fun> • All recursive functions in Ocaml need the rec keyword
Binding issues • Consider: # let foo x = x+5;; val foo : int -> int = <fun> # let bar = foo;; val bar : int -> int = <fun> # let foo x = x+6;; val foo : int -> int = <fun> # foo 1;; - : int = 7 # bar 1;; - : int = 6 # • Note that although foo has been redefined, bar still uses the old foo
Mutually recursive functions • When two functions call each other recursively let rec even x = if x = 0 then true else odd (x-1) and odd x = if x = 0 then false else even (x-1);; • I needed mutually recursive functions for the nfasimulate function
let … in syntax • Allows you to declare local values: let fun foo x = let y = 5 in let z = square x in x*y*z;; • Can nest them as many as desired • Can also declare local functions this way: let quad x = let square x = x * x in (square x) * (square x);;
match keyword • Allows you to do both case statements and breaking apart of complex data types let rec countlist list = match list with [] -> 0 | _ :: tail -> 1 + (countlist tail);; • The ‘_’ is used when you want to match something, but don’t plan on using it in the match
The match keyword, continued • Consider the transitions part of the FAs • Type is (string * string * string) list let fun foo trans = match trans with [] -> ... | (a, b, “a”) :: tail -> ... | (a, b, c) :: tail -> ... • Most of my functions used the match keyword to break apart the parts of the FA parameter
User defined types • User can define custom types type btree = Node of int * btree * btree | Leaf;; let mytree = Node (1, Node(2, Leaf, Leaf), Leaf);; let rec countnodes tree = match tree with Leaf -> 0 | Node (_, lefttree, righttree) -> 1 + (countnodes lefttree) + (countnodes righttree);; • Type name (here ‘btree’) must be all lower case
More on user defined types • If you want a function to return a float or an int or a string: # type multi = Int of int | Float of float | String of string;; type multi = Int of int | Float of float | String of string # Int(7);; - : multi = Int 7 # Float(3.4);; - : multi = Float 3.4 # String "st";; - : multi = String "st" # let foo x = if x = 0 then Int 0 else if x < 0 then Float 0.0 else String "0";; val foo : int -> multi = <fun> #
The #use command • To read in a file into Ocaml use #use: # #use "fa.ml";; • This will read in (and evaluate) the file fa.ml • You can then use the functions in the interpreter • This is probably the only ‘function’ with side-effects that you’ll be using
Generics • Consider: # let rec countlist list = match list with [] -> 0 | _ :: tail -> 1 + (countlist tail);; val countlist : 'a list -> int = <fun> # • This function works on any type of list • the ‘a (read as alpha) means that any type can be substituted for alpha • Including (string list * char) list, for example • The List.hd function has type ‘a list -> ‘a • Meaning the type returned is the type of list read in
Using modules • For the homework, you are allowed to use any functions in the List or String modules • You can refer to them as List.hd, List.tl, etc. • If you do anopen List;;you can then refer to them just as hd or tl • Note if you open the String module, it defines a new compare method • Which will cause issues if you are using ‘sort compare’ from the List module (see next slide)
List module functions • map • Applies the passed function to each element in the list • Usage: map square [1;2;3;4];; • Result: [1;4;9;16] • sort • Allows you to sort elements in a list • First parameter must be the comparitor • The function that tells how to order individual elements • Use ‘compare’ for this • Note that if you open the String module, there is a different compare function, which will prevent the following from working • Usage: sort compare [4;3;2;1];; • Etc. • See the module documentation for more List module functions
Debugging Ocaml functions • So how to debug the functions? • You can’t really use print statements • There is an ocaml debugger • Called ocamldebug • Installs with the Cygwin ocaml package • Build in little steps • And test each step to make sure it works • Make sure the types of your functions match what you expect them to be • Do a lot of hand-tracing • It’s a pain, but it will find your bug • You can use the interpreter for this…
Ocaml gotchas • Using ;; to terminate functions (not a single ;) • ; vs , for lists vs tuples • #use “fa.ml”;; to read in a file • (* Comments are enclosed in star-parenthesis *) • The difference between :: and @ • String.compare overwriting the default compare • + vs +. (and similar for * / -)
Function example: uniq • This method takes in a sorted list, and removes any duplicates • As the list is sorted, any duplicates would be next to each other • Name is short-hand for unique • Operates like the Un*x command of the same name let rec uniq list = match list with [] -> [] | h1 :: [] -> h1 :: [] | h1 :: h2 :: tail -> if h1 = h2 then uniq (h2 :: tail) else h1 :: uniq (h2 :: tail);;
Homework hints • For the NFA to DFA conversion • When dealing with the DFA states, I found it easier to have the state names be sets (lists) for the intermediate functions • Use uniq and List.sort to ensure that the sets (lists) are equivalent • You will have to write intermediate steps for each part of the conversion algorithm