Introduction to OCaml Slides prepared by Matt Gruskin Some material borrowed from the CIS 500 lecture notes Functional Programming • Persistent (immutable) data structures – No assignments in pure functional programming! • Recursion – No loops in pure functional programming! • Higher-order functions – Functions that take other functions as arguments or return functions as results • OCaml is not a pure functional language – OCaml does have for and while loops, but I won’t show them here Let statements • Give a name to an expression or define a function # let pi = 3.1415;; val pi : float = 3.1415 # pi;; -: float = 3.1415 # let circle_area r = pi *. r *. r;; val circle_area : float -> float = <fun> # circle_area 4.0;; - : float = 50.264 >>> pi = 3.1415; >>> pi 3.1415000000000002 >>> def circle_area(r): ... return pi * r * r; ... >>> circle_area(4); 50.264000000000003 Syntax differences from Python • Method calls – Parameters separated by spaces instead of commas… and no parentheses around parameter list – Python: method(a,b,c) – OCaml: method a b c Type Inference • Types are inferred (like Python) – So you don’t have to give parameters types • Unlike Python, types are inferred BEFORE runtime – When entering a function in the toplevel you’ll see Param type -> param type -> … -> return type – This is why different operators are needed for different numeric types # let circle_area r = pi *. r *. r;; val circle_area : float -> float = <fun> Lists • • • • Similar to lists in Python Unlike Python, implemented as linked lists Cons – hd :: tl (hd is first element, tl is a list) Unlike Python, lists are homogeneous – You can do [5,’a’,True] in Python but you can’t do [5;’a’;true] in OCaml # let l = [1;3;5;7];; val l : int list = [1; 3; 5; 7] # l;; - : int list = [1; 3; 5; 7] >>> l = [1,3,5,7] >>> l [1, 3, 5, 7] # [-1;0] @ l;; - : int list = [-1; 0; 1; 3; 5; 7] >>> [-1,0] + l [-1, 0, 1, 3, 5, 7] # List.hd l;; - : int = 1 # List.tl l;; -: int list = [3; 5; 7] # List.nth l 2;; - : int = 5 >>> 1 >>> [3, >>> 5 l[0] l[1:] 5, 7] l[2] Recursive functions let rec gcd a if a=0 then else if a>b else gcd (b b = b then gcd b a mod a) a def gcd(a,b): if a == 0: return b if a > b: return gcd(b,a) return gcd(b % a, a) let rec repeat k n = if n=0 then [] else k :: repeat k (n-1) def repeat(k,n): if n == 0: return [] else: return [k] + repeat(k,n-1) # repeat 1 4;; - : int list = [1; 1; 1; 1] >>> repeat(1,4) [1, 1, 1, 1] Pattern Matching • Alternative to imperative statements like if, switch let rec gcd a if a=0 then else if a>b else gcd (b b = b then gcd b a mod a) a let rec gcd a b = match a with 0 -> b | x when x > b -> gcd b a | _ -> gcd (b mod a) a Tail Recursion • Result of recursive call is also result of entire function • In OCaml, tail recursion uses constant stack space let rec rev l = match l with [] -> [] | hd :: tl -> (rev tl) @ [hd] def rev(l): if l == []: return [] else: return rev(l[1:]) + [l[0]] let rec tailrev l r = match l with [] -> r | hd :: tl -> tailrev tl (hd :: r) def tailrev(l,r=[]): if l == []: return r else: return tailrev(l[1:],[l[0]]+r) let rec tailrev ?(r=[]) l = match l with [] -> r | hd :: tl -> tailrev ~r:(hd :: r) tl Functions as parameters # let l = [3;5;7];; val l : int list = [3; 5; 7] >>> l = [3,5,7] # List.map (fun x -> x*2) l;; -: int list = [6; 10; 14] >>> [6, >>> [6, # List.fold_left (fun x y -> x + y) 0 l;; -: int = 15 >>> reduce(lambda x,y:x+y,l,0) 15 # List.filter (fun x -> x > 4) l;; - : int list = [5; 7] >>> filter(lambda x: x>4,l) [5, 7] # List.iter (Printf.printf "%i\n") l;; 3 5 7 : unit = () >>> for x in l: ... print x ... 3 5 7 map(lambda x: x*2,l) 10, 14] [x*2 for x in l] 10, 14] Quicksort PYTHON def sort(a): if a == []: return [] else: pivot = a[0] left = [x for x in a if x < pivot] right = [x for x in a[1:] if x >= pivot] return sort(left) + [pivot] + sort(right) OCaml let rec quicksort l = match l with [] -> [] | pivot :: t -> let left = List.filter (fun x -> x < pivot) t in let right = List.filter (fun x -> x >= pivot) t in (quicksort left) @ [pivot] @ (quicksort right) Palindrome PYTHON def palindrome(s): if len(s) <= 1: return True return s[0] == s[-1] and palindrome(s[1:-1]) OCaml let rec palindrome s = s = (tailrev s)