1 / 28

Higher-order functions in OCaml

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

hazina
Télécharger la présentation

Higher-order functions in OCaml

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Higher-order functions in OCaml

  2. 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

  3. 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

  4. 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!

  5. Doubling list elements with map # let double x = 2 * x;; val double : int -> int = <fun> # let doubleAll lst = map double lst;; val doubleAll : int list -> int list = <fun> # 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

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

  7. 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 = <fun> • map double looks like a function...how?

  8. 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)

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

  10. Writing a curried function I • # let add x y = x + y;; • val add : int -> int -> int = <fun> • 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

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

  12. Defining higher-order functions I # let apply1 (f, x) = f x;; val apply1 : ('a -> 'b) * 'a -> 'b = <fun> # 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

  13. Defining higher-order functions II # let apply2 f x = f x;; val apply2 : ('a -> 'b) -> 'a -> 'b = <fun> # 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

  14. 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

  15. 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 = <fun> # span even [2;4;6;7;8;9;10];; - : int list = [2; 4; 6]

  16. 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

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

  18. 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

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

  20. 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 = <fun> # span2 even [2;4;6;7;8;9;10];; - : int list * int list = [2; 4; 6], [7; 8; 9; 10]

  21. 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]

  22. 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, andelements > 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]

  23. 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 = <fun> # 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]

  24. 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 = <fun> • This applies a (boolean) test to each adjacent pair of elements and "ANDs" the results • Can we generalize this function?

  25. 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

  26. 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)

  27. 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

  28. The End

More Related