CMSC330

Higher Order Functions

Higher Order Functions

Higher Order Programming
Higher Order Functions
Map and Fold

Higher Order Programming

Higher Order Programming

Recall Code blocks and Procs in Ruby


p = Proc.new {|x| x + 1}
          

We can pass procs in as arguments


p = Proc.new {|x| x + 1}
def f(x)
  puts x.call(3)
end
          

Treating code and functions as data

We can do this and more in OCaml

Higher Order Functions

Anonymous Functions

Anonymous function: function not bound to a variable

Syntax: fun x1 ... xn -> e


fun x -> x + 3
          

Technically functions are anonymous functions with syntactic sugar


let add3 = fun x -> x + 3
          

Recall Referential Transparency

Functions are expressions


(* function_call.ml *)
let add3 x = x + 3;;
let add3 = fun x -> x + 3;;
add3 4;;
(fun x -> x + 3) 4;;
          

So treat them like expressions


let plus3 = add3;;
plus3 4;;
          

Can pass in functions as arguments


(* function_application.ml *)
let apply f x = f x;;
(* type syntax: ('a -> 'b) -> 'a -> 'b *)
let sub3 x = x - 3;;
apply_to_int add3 4;;
apply_to_int sub3 4;;
          

Recall Functions have types


(* function_types.ml *)
let apply_to_int f x = f (x+1);;
          

Only functions of type int -> 'a


apply_to_int (fun x -> x +. 3) 4 (* error *)
          

Map and Fold

Map

Common and useful HOFs exist


let rec map f l = match l with
[]-> []
|h::t -> (f h)::(map f t)
          

map: ('a -> 'b) -> 'a list -> 'b list
          
  • take in a function and a list
  • The list can be thought as the domain
  • the function is applied to each item in the domain
  • Returns new list of corresponding output
  • Basically maps domain to co-domain
Map

Basically maps domain to co-domain


let rec map f l = match l with
[]-> []
|h::t -> (f h)::(map f t)
          

(* map.ml *)
let add1 x = x + 1;;
let x2 x = x + x;;
let is_even x = x mod 2 = 0;;
let lst = [1;2;3];;
map add1 lst;;
map x2 lst;;
map is_even lst;;
let fs = [add1;x2;(fun x -> -x)]
map (fun f -> map f lst) fs;;
          

Consider the following family of functions


(* ff.ml *)
let rec concat lst = match lst with 
[]-> ""
|h::t -> h^(concat t)

let rec sum lst = match lst with 
[]-> 0
|h::t -> h+(sum t)

let rec product lst = match lst with 
[]-> 1
|h::t -> h*(productt)

let rec length lst = match lst with 
[]-> 0
|_::t -> 1+(length t)

let rec rev lst = match lst with 
[]-> []
|h::t -> (rev t) @ [h];;

let rec filter lst compare_fun = match lst with 
[]-> []
|h::t -> if compare_fun h then 
          h::(filter t compare_fun) else 
          filter t compare_fun;;
          

Aggregates a list to a single value

Fold

Consider the following Fibinocci


(* fib.ml *)
let rec fib n = 
if n < 2 then 1 else (fib (n-1)) - (fib (n-2));;
          
  • imperfect compiler: too many stack frames
  • Not tail Recursive

Let's fix this


(* fib-1.ml *)
let rec fib n a b = 
if n = 0 then a else fib (n-1) (a+b) a;;
          
Fold

Fold will incorporate

  • Aggregating a list to a single value
  • Reducing Stack Frames

(* fold.ml *)
let rec fold f a l= match l with 
[]-> a
|h::t-> fold f (f a h) t;;
          

fold: ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a
          
Foldr

Foldr only uses

  • Aggregating a list to a single value

But, the order of evaluation is reversed


(* foldr.ml *)
let rec foldr f l a = match l with 
[]-> a
|h::t-> f h (foldr f t a);;
          

foldr: ('b -> 'a -> 'a) -> 'b list -> 'a -> 'a
          

(* fold.ml *)
let rec fold f a l= match l with 
[]-> a
|h::t-> fold f (f a h) t;;
          

(* foldr.ml *)
let rec foldr f l a = match l with 
[]-> a
|h::t-> f h (foldr f t a);;
          

Be careful with non-commutative functions


let diff x y = x - y in 
(fold diff 0 [1;2;3]) = (foldr diff [1;2;3] 0);;
          

let rec fold f a l = match l with
[]-> a 
|h::t-> fold f (f a h) t
          

(* fold.ml *)
let concat a h = a^h;;
let sum a h = a + h;;
let product a h = a * h;;
let length a _ = a + 1;;
let rev a h = a @ [h];;
let filter f lst = 
let filter_helper a h = if f h then h::a else a in
fold filter_helper [] lst;;
let is_even x = x mod 2 = x;;
filter is_even [1;2;3;4;5];;
          

We can use map and fold together for cool stuff

Can we count 1s in a 2d matrix?


(* count 1s in a 2d matrix *)
(* based on what we already have (inefficent) *)
let countones lst =
(* take out non-ones from 1-d list *)
let get1s lst = filter (fun x -> x = 1) lst in
(* take out non-ones from each sublist *)
let ones = map get1s lst in
(* get the length of each 1 list *)
let counts = map (fold length 0) ones in
(* add up the lengths *)
let total = fold sum 0 counts in total;;
val countones : int list list -> int = 

(* or we can do the easy way *)
let count1 lst = fold (fun a h -> if h = 1 then a+1 else a) 0 lst;;
let count1s lst = fold (+) 0 (map count1 lst)
          

How could we rotate a matrix?