Higher-order functions in OCaml. Higher-order functions A first-order function is one whose parameters and result are all "data" A second-order function.

Presentation on theme: "Higher-order functions in OCaml. Higher-order functions A first-order function is one whose parameters and result are all "data" A second-order function."— Presentation transcript:

Higher-order functions in OCaml

Higher-order functions A first-order function is one whose parameters and result are all "data" A second-order function has one or more first-order functions as parameters or result In general, a higher-order function has one or more functions as parameters or result OCaml supports higher-order functions

Doubling, revisited # let rec doubleAll = function [] -> [] | (h::t) -> (2 * h)::(doubleAll t);; val doubleAll : int list -> int list # doubleAll [1;2;3;4;5];; - : int list = [2; 4; 6; 8; 10] This is the usual heavy use of recursion It's time to simplify things

map map applies a function to every element of a list and returns a list of the results map f [x, y, z] returns [f x, f y, f z] Notice that map takes a function as an argument Ignore for now the fact that map appears to take two arguments!

Doubling list elements with map # let double x = 2 * x;; val double : int -> int = # let doubleAll lst = map double lst;; val doubleAll : int list -> int list = # doubleAll [1;2;3;4;5];; - : int list = [2; 4; 6; 8; 10] The definition of doubleAll is simpler, but......now we need to expose double to the world

Anonymous functions An anonymous function has the form (fun parameter -> body ) Now we can define doubleAll as let doubleAll lst = map (fun x -> 2*x) lst;; This final definition is simple and doesn't require exposing an auxiliary function

The mysterious map ML functions all take a single argument, but... map double [1;2;3] works map (double, [1;2;3]) gives a type error Even stranger, (map double) [1;2;3] works! # map double;; - : int list -> int list = map double looks like a function...how?

Currying In OCaml, functions are values, and there are operations on those values Currying absorbs a parameter into a function, creating a new function map takes one argument (a function), and returns one result (also a function)

Order of operations let add (x, y) = x + y;; –# val add : int * int -> int = But also consider: # let add x y = x + y;; –val add : int -> int -> int = add x y is grouped as (add x) y and int -> int -> int as int -> (int -> int)

Writing a curried function I # let add x y = x + y;; –val add : int -> int -> int = –That is, add has type int -> (int -> int) –Our new add takes an int argument and produces an (int -> int) result (add 5) 3;; (* currying happens *) - : int = 8

Writing a curried function II let addFive = add 5;; –# val addFive : int -> int = –Notice this is a val; we are manipulating values # addFive 3;; (* use our new function *) –- : int = 8

Defining higher-order functions I # let apply1 (f, x) = f x;; val apply1 : ('a -> 'b) * 'a -> 'b = # apply1 (tl, [1;2;3]);; - : int list = [2; 3] But: # apply1 tl [1;2;3];; –Characters 7-9: This expression has type 'a list -> 'a list but is here used with type ('b -> int list -> 'c) * 'b

Defining higher-order functions II # let apply2 f x = f x;; val apply2 : ('a -> 'b) -> 'a -> 'b = # apply2 tl [1;2;3];; - : int list = [2; 3] # apply2 (tl, [1;2;3]);; Characters 8-19: This expression has type ('a list -> 'a list) * int list but is here used with type 'b -> 'c Advantage: this form can be curried

A useful function: span span finds elements at the front of a list that satisfy a given predicate Example: span even [2;4;6;7;8;9;10] gives [2, 4; 6] span isn't a built-in; we have to write it

Implementing span # let rec span f lst = if f (hd lst) then (hd lst)::span f (tl lst) else [];; val span : ('a -> bool) -> 'a list -> 'a list = # span even [2;4;6;7;8;9;10];; - : int list = [2; 4; 6]

Extending span: span2 span returns the elements at the front of a list that satisfy a predicate Suppose we extend it to also return the remaining elements We can do it with the tools we have, but more tools would be convenient

Generalized assignment # let (a, b, c) = (8, 3, 6);; –val a : int = 8 val b : int = 3 val c : int = 6 # let (x::xs) = [1;2;3;4];; –(* Non-exhaustive match warning deleted *) val x : int = 1 val xs : int list = [2; 3; 4] Generalized assignment is especially useful when a function returns a tuple

Defining local values with let let declaration in expression let decl1 in let decl2 in expression # let a = 5 in let b = 10 in a + b;; - : int = 15 let helps avoid redundant computations

Example of let # let circleArea radius = let pi = 3.1416 in let square x = x *. x in pi *. square radius;; val circleArea : float -> float = # circleArea 10.0;; - : float = 314.160000

Implementing span2 # let rec span2 f lst = if f (hd lst) then let (first, second) = span2 f (tl lst) in ((hd lst :: first), second) else ([], lst);; val span2 : ('a -> bool) -> 'a list -> 'a list * 'a list = # span2 even [2;4;6;7;8;9;10];; - : int list * int list = [2; 4; 6], [7; 8; 9; 10]

Another built-in function: partition Partition breaks a list into two lists: those elements that satisfy the predicate, and those that don't Example: # partition even [2;4;6;7;8;9;10];; - : int list * int list = [2; 4; 6; 8; 10], [7; 9]

Quicksort Choose the first element as a pivot: –For [3;1;4;1;5;9;2;6;5] choose 3 as the pivot Break the list into elements pivot: –[1; 1; 2] and [4; 5; 9; 6; 5] Quicksort the sublists: –[1; 1; 2] and [4; 5; 5; 6; 9] Append the sublists with the pivot in the middle: –[1; 1; 2; 3 ; 4; 5; 5; 6;, 9]

Quicksort in ML let rec quicksort = function [] -> [] | (x::xs) -> let (front, back) = partition (fun n -> n <= x) xs in (quicksort front) @ (x::(quicksort back));; val quicksort : 'a list -> 'a list = # quicksort [3;1;4;1;5;9;2;6;5;3;6];; - : int list = [1; 1; 2; 3; 3; 4; 5; 5; 6; 6; 9]

Testing if a list is sorted The following code tests if a list is sorted: # let rec sorted = function [] -> true | [_] -> true | (x::y::rest) -> x <= y && sorted (y::rest);; –val sorted : 'a list -> bool = This applies a (boolean) test to each adjacent pair of elements and "ANDs" the results Can we generalize this function?

Generalizing the sorted predicate let rec sorted list = match list with [] -> true | [_] -> true | (x::y::rest) -> x <= y && sorted (y::rest);; The underlined part is the only part specific to this particular function We can replace it with a predicate passed in as a parameter

pairwise let rec pairwise f list = match list with [] -> true | [_] -> true | (x::y::rest) -> (f x y) && pairwise f (y::rest);; Here are the changes we have made: –Changed the name from sorted to pairwise –Added the parameter f in two places –changed x <= y to (f x y)

Using pairwise # pairwise (<=) [1;3;5;5;9];; - : bool = true # pairwise (<=) [1;3;5;9;5];; - : bool = false # pairwise (fun x y -> x = y - 1) [3;4;5;6;7];; - : bool = true # pairwise (fun x y -> x = y - 1) [3;4;5;7];; - : bool = false

The End

Download ppt "Higher-order functions in OCaml. Higher-order functions A first-order function is one whose parameters and result are all "data" A second-order function."

Similar presentations