1 / 31

Higher-order functions in ML

Higher-order functions in ML. 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

keahi
Download Presentation

Higher-order functions in ML

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 ML

  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 • ML supports higher-order functions

  3. Doubling, revisited • fun doubleAll [ ] = [ ]| doubleAll (h::t) = 2 * h :: (doubleAll t); • val doubleAll : int list -> int list = fn • doubleAll [1,2,3,4,5]; • val it : 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 • fun double x = 2 * x; • fun doubleAll lst = map double lst; • val doubleAll : int list -> int list = fn • doubleAll [1,2,3,4,5]; • val it : 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 (fnparameter => body) • Now we can define doubleAll asfun doubleAll lst = map (fn x => x+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; • val it : int list -> int list = fn • map double looks like a function...how?

  8. Currying • In ML, 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 • fun add (x, y) = x + y; • val add : (int * int) -> int = fn • But also consider: • fun add x y = x + y; • val add : int -> int -> int = fn • add x y is grouped as (add x) y • and int -> int -> int as int -> (int -> int)

  10. Writing a curried function I • fun add x y = x + y; • val add : int -> int -> int = fn • That is, add : int -> (int -> int) • Our new add takes an int argument and produces an (int -> int) result • (add 5) 3; (* currying happens *) • val it : int = 8

  11. Writing a curried function II • MLWorks> val add5 = add 5; • val add5 : int -> int = fn • Notice the use of val; we are manipulating values • MLWorks> add5 3; (* use our new fn *) • val it : int = 8

  12. Function composition • The function composition operator is the infix lowercase letter o • (f o g) x gives the same result as f(g(x)) • But composition gives you a way to bundle the functions for later use; f(g(x)) requires an argument x right now • val h = f o g; is perfectly legal ML

  13. Defining higher-order functions I • fun apply1(f, x) = f(x); • val apply1 : (('a -> 'b) * 'a) -> 'b = fn • apply1 (tl, [1,2,3]); • val it : int list = [2, 3] • But: • apply1 tl [1,2,3]; • error: Function applied to argument of wrong type

  14. Defining higher-order functions II • fun apply2 f x = f(x); • val apply2 : ('a -> 'b) -> 'a -> 'b = fn • apply2 tl [1,2,3]; • val it : int list = [2, 3] • apply2 (tl, [1,2,3]); • error: Function applied to argument of wrong type • Advantage: this form can be curried

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

  16. Implementing span • fun span f L = if f(hd L) then (hd L) :: span f (tl L) else []; • span even [2,4,6,7,8,9,10]; • val it : int list = [2, 4, 6]

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

  18. Generalized assignment • val (a, b, c) = (8, 3, 6); • val a : int = 8val b : int = 3val c : int = 6 • val x::xs = [1,2,3,4]; • val x : int = 1 val xs : int list = [2, 3, 4] • Generalized assignment is especially useful when a function returns a tuple

  19. Defining local values with let • let declaration ; declaration ; . . .in expressionend • let helps avoid redundant computations

  20. Example of let fun circleArea (radius) = let val pi = 3.1416; fun square x = x * x in pi * square (radius) end;

  21. Implementing span2 • fun span2 f list = if f(hd list) then let val (first, second) = span2 f (tl list) in ((hd list :: first), second) end else ([], list); • val span2 : ('a -> bool) -> 'a list -> ('a list * 'a list) = fn • span2 even [2,4,6,7,8,9,10]; • val it : (int list * int list) = ([2, 4, 6], [7, 8, 9, 10])

  22. 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]; • val it : (int list * int list) = ([2, 4, 6, 8, 10], [7, 9])

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

  24. Quicksort in ML • fun quicksort [ ] = [ ]| quicksort (x :: xs) = let val (front, back) = partition (fn n => n <= x) xs in (quicksort front) @ (x :: (quicksort back)) end; • val quicksort : int list -> int list = fn • quicksort [3,1,4,1,5,9,2,6,5,3,6]; • val it : int list = [1, 1, 2, 3, 3, 4, 5, 5, 6, 6, ..]

  25. foldl • foldl op basis listrepeatedly computes (element op basis), starting with the basis and using the list elements starting from the left • foldl (op -) 10000 [1, 20, 300, 4000]; • val it : int = 13719 • (4000 - (300 - (20 - (1 - 10000)))); • val it : int = 13719

  26. foldr • foldr op basis listrepeatedly computes (element op basis), starting with the basis and using the list elements starting from the right • foldr (op -) 10000 [1, 20, 300, 4000]; • val it : int = 6281 • (1 - (20 - (300 - (4000 - 10000)))); • val it : int = 6281

  27. Testing if a list is sorted • The following code tests if a list is sorted: • fun sorted [] = true| sorted [_] = true| sorted (x::y::rest) = x <= y andalso sorted (y::rest); • This applies a (boolean) test to each adjacent pair of elements and "ANDs" the results • Can we generalize this function?

  28. Generalizing the sorted predicate • fun sorted [] = true| sorted [_] = true| sorted (x::y::rest) =x <= y andalso 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

  29. pairwise • fun pairwise f [] = true| pairwise f [_] = true| pairwise f (x::y::rest) = f(x,y) andalso pairwise f (y::rest); • Here are the changes we have made: • Changed the name from sorted to pairwise • Added the parameter f • changed x <= y to f(x, y)

  30. Using pairwise • pairwise (op <=) [1, 3, 5, 5, 9]; • val it : bool = true • pairwise (op <=) [1, 3, 5, 9, 5]; • val it : bool = false • pairwise (fn (x, y) => x = y - 1) [3,4,5,6,7]; • val it : bool = true • pairwise (fn (x, y) => x = y - 1) [3,4,5,7]; • val it : bool = false

  31. The End

More Related