Download presentation

Presentation is loading. Please wait.

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 doubleAll [1,2,3,4,5];**

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; But also consider:**

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 = ; 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, and 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 (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 -) [1, 20, 300, 4000]; val it : int = 13719 ( (300 - (20 - ( ))));

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 -) [1, 20, 300, 4000]; val it : int = 6281 (1 - (20 - (300 - ( ))));

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]; pairwise (fn (x, y) => x = y - 1) [3,4,5,7];

31
The End

Similar presentations

OK

52 16 21 77 33 82 69 42 54 18 28 73 S: Application of quicksort on an array of ints: partitioning.

52 16 21 77 33 82 69 42 54 18 28 73 S: Application of quicksort on an array of ints: partitioning.

© 2017 SlidePlayer.com Inc.

All rights reserved.

Ads by Google