Next Tail Recursion: Factorial More on recursion Higher-order functions takg and returng functions Along the way, will see map and fold let rec fact n = if n<=0 then 1 else n * fact (n-1);; 1 2 How does it execute? let rec fact n = if n<=0 then 1 else n * fact (n-1);; fac 3;; Tail recursion Tail recursion: recursion where all recursive calls are immediately followed by a return other words: not allowed to do anythg between recursive call and return 3 4 Tail recursive factorial Tail recursive factorial let fact x = let fact x = let rec helper x curr = if x <= 0 then curr else helper (x - 1) (x * curr) helper x 1;; 5 6 1
How does it execute? Tail recursion let fact x = let rec helper x curr = if x <= 0 then curr else helper (x - 1) (x * curr) helper x 1;; Tail recursion: for each recursive call, the value of the recursive call is immediately returned other words: not allowed to do anythg between recursive call and return fact 3;; Why do we care about tail recursion? it turns out that tail recursion can be optimized to a simple loop 7 8 Compiler can optimize! Tail recursion summary let fact x = let rec helper x curr = if x <= 0 fact(x) { curr := 1; while (1) { if (x <= 0) Tail recursive calls can be optimized as a jump then curr else helper (x - 1) (x * curr) } then { return curr } else { x := x 1; curr := (x * curr) } Part of the language specification of some languages (ie: you can count on the compiler to optimize tail recursive calls) helper x 1;; recursion! Loop! 9 10 max function max function let max x y = if x < y then y else x;; let max x y = if x < y then y else x;; (* return max element of list l *) let list_max l = 11 (* return max element of list l *) let list_max l = h::t -> helper (max curr h) t helper 0 l;; 12 2
concat function concat function (* concatenate all strgs a list *) let concat l = (* concatenate all strgs a list *) let concat l = h::t -> helper (curr ^ h) t helper "" l;; 13 14 What s the pattern? fold, the general helper func! let list_max l = h::t -> helper (max h curr) t helper 0 l;; (* to help us see the pattern: *) let list_max l = h::t -> helper (max h curr) t helper 0 l;; let concat l = h::t -> helper (curr ^ h) t helper "" l;; 15 (* fold, the coolest function there is! *) let rec fold f curr l = 16 fold fold (* fold, the coolest function there is! *) let rec fold f curr l = h::t -> fold f (f curr h) t;; (* fold, the coolest function there is! *) let rec fold f curr l = h::t -> fold f (f curr h) t;; 17 18 3
Examples of fold Examples of fold let list_max = let list_max = fold max 0;; let concat = let concat = fold (^) "";; let multiplier = let multiplier = fold (*) 1;; 19 20 Examples of fold Examples of fold let fact n = multiplier (terval 1 n);; let cons x y = y::x;; let f = fold cons [];; (* same as: let f l = fold cons [] l *) Notice how all the recursion is buried side two functions: terval and fold! 21 22 Examples of fold More recursion: terval let cons x y = y::x;; let f = fold cons [];; (* same as: let f l = fold cons [] l *) (* return a list that contas the tegers i through j clusive *) let rec terval i j = 23 24 4
terval terval function with it fn (* return a list that contas the tegers i through j clusive *) let rec terval i j = if i > j then [] else i::(terval (i+1) j);; (* return a list that contas the elements f(i), f(i+1),... f(j) *) let rec terval_it i j f = 25 26 terval function with it fn terval function aga (* return a list that contas the elements f(i), f(i+1),... f(j) *) let rec terval_it i j f = if i > j then [] else (f i)::(terval_it (i+1) j f);; (* our regular terval function terms of the one with the it function *) let rec terval i j = 27 28 terval function aga Interval function yet aga! (* our regular terval function terms of the one with the it function *) let rec terval i j = terval_it i j (fun x -> x);; (* let's change the order of parameters... *) let rec terval_it f i j = if i > j then [] else (f i)::(terval_it f (i+1) j);; (* now can use curryg to get terval function! *) let terval = terval_it (fun x -> x);; 29 30 5
Function Curryg Function Curryg vs tuples In general, these two are equivalent: let f = fun x1 -> -> fun xn -> e Tuple version: fn defition let f (x1,,xn) = e fn call f (x1,,xn) let f x1 xn = e Multiple argument functions by returng a function that takes the next argument Named after a person (Haskell Curry) Curried version: fn defition let f x1 xn = e fn call f x1 xn 31 32 Function Curryg vs tuples map Consider the followg: let lt x y = x < y; (* return the list contag f(e) for each element e of l *) let rec map f l = Could have done: let lt (x,y) = x<y; But then no testers possible In general: Curryg allows you to set just the first n params (where n smaller than the total number of params) 33 34 map map (* return the list contag f(e) for each element e of l *) let rec map f l = [] -> [] h::t -> (f h)::(map f t);; let cr x = x+1;; let map_cr = map cr;; map_cr (terval (-10) 10);; 35 36 6
composg functions (f o g) (x) = f(g(x)) (* return a function that given an argument x applies f2 to x and then applies f1 to the result*) let compose f1 f2 = composg functions (f o g) (x) = f(g(x)) (* return a function that given an argument x applies f2 to x and then applies f1 to the result*) let compose f1 f2 = fun x -> (f1 (f2 x));; (* another way of writg it *) let compose f1 f2 x = f1 (f2 x);; 37 38 Higher-order functions! Higher-order functions! let map_cr_2 = compose map_cr map_cr;; map_cr_2 (terval (-10) 10);; let map_cr_3 = compose map_cr map_cr_2;; map_cr_3 (terval (-10) 10);; let map_cr_3_pos = compose pos_filer map_cr_3;; map_cr_3_pos (terval (-10) 10);; (compose map_cr_3 pos_filer) (terval (-10) 10);; let map_cr_2 = compose map_cr map_cr;; map_cr_2 (terval (-10) 10);; let map_cr_3 = compose map_cr map_cr_2;; map_cr_3 (terval (-10) 10);; let map_cr_3_pos = compose pos_filer map_cr_3;; map_cr_3_pos (terval (-10) 10);; (compose map_cr_3 pos_filer) (terval (-10) 10);; 39 Instead of manipulatg lists, we are manipulatg the list manipulators! 40 Exercise 1 let rec filter f l = [] -> [] h::t -> let t = filter f t if f h then h::t else t Exercise 1 val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a let partition f l = let neg f x = not (f x) let partition f l = (filter f l, filter (neg f) l) This implementation is not ideal, sce it unnecessarily processes the list twice. Rewrite partition so that it is a sgle call to fold_left, so the put list is processed only once. Recall: val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a 41 42 7
Exercise 1 Solution val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a let partition f l = let fold_fn (pass,passnot) x = if f x then (pass@[x], passnot) else (pass, passnot@[x]) List.fold_left fold_fn ([],[]) l;; Exercise 2 val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a val map : ('a -> 'b) -> 'a list -> 'b list Implement map usg fold: let map f l = 43 44 Exercise 2 Solution val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a val map : ('a -> 'b) -> 'a list -> 'b list Benefits of higher-order functions Identify common computation patterns Implement map usg fold: let map f l = List.fold_left (fun acc x -> acc@[f x]) [] l Iterate a function over a set, list, tree Accumulate some value over a collection Pull out (factor) common code: Computation Patterns Re-use many different situations 45 46 Funcs takg/returng funcs Higher-order funcs enable modular code Each part only needs local formation Data Structure Client Uses list Uses meta-functions: map,fold,filter Data Structure Library list Provides meta-functions: map,fold,filter Different way of thkg Free your md -Morpheus Different way of thkg about computation Manipulate the manipulators With locally-dependent funs to traverse, accumulate over (lt h), square etc. lists, trees etc. Without requirg Implement. Meta-functions don t need client details of data structure fo 47 48 8