Download presentation

Presentation is loading. Please wait.

Published byJaydon Oxley Modified over 2 years ago

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 (fn parameter => body ) Now we can define doubleAll as fun 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 = 8 val b : int = 3 val 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 expression end 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: –[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 list repeatedly 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 list repeatedly 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

Similar presentations

© 2017 SlidePlayer.com Inc.

All rights reserved.

Ads by Google