<<

Evaluating declarations

Programming Languages and n Evaluation uses an environment ρ (CS 421) n To evaluate a (simple) declaration let x = e

n Evaluate expression e in ρ to value v n Then update ρ with x v: {x → v} + ρ Elsa L Gunter 2112 SC, UIUC http://courses.engr.illinois.edu/cs421 Based in part on slides by Mattox Beckman, as updated by Vikram Adve and Gul Agha

9/11/14 1 9/11/14 2

Evaluating expressions Eval of App with Closures in OCaml n Evaluation uses an environment ρ 1. Evaluate the right term to values, (v1,…,vn) n A constant evaluates to itself 2. In environment ρ, evaluate left term to n To evaluate an variable, look it up in ρ (ρ(v)) closure, = <(x1,…,xn) → b, ρ> n To evaluate uses of +, _ , etc, eval args, 3. Match (x1,…,xn) variables in (first) argument then do operation with values (v1,…,vn) n Function expression evaluates to its closure 4. Update the environment ρ to n To evaluate a local dec: let x = e1 in e2 ρ’ = {x1 → v1,…, xn →vn}+ ρ

n Eval e1 to v, then eval e2 using {x → v} + ρ 5. Evaluate body b in environment ρ’

9/11/14 3 9/11/14 4

OCaml Example 1 OCaml Example 1

# (print_string "a"; # (print_string "a"; (fun x -> (print_string "b"; (fun x -> (print_string "b"; (fun y -> (print_string "c"; (fun y -> (print_string "c"; x + y))))) x + y))))) (print_string "d"; 3) (print_string "d"; 3) (print_string "e"; 5);; (print_string "e"; 5);;

edabc- : int = 8 # 9/11/14 5 9/11/14 6 # let f = (print_string "a"; Your turn now (fun x -> (print_string "b"; (fun y -> (print_string "c"; x + y))))) in Try Problem 1 on HW3 let u = (print_string "d"; 3) in let g = f u in let v = (print_string "e"; 5) in g v;;

9/11/14 7 9/11/14 8

Higher Order Functions

# let f = (print_string "a"; n A function is higher-order if it takes a function as an argument or returns one as (fun x -> (print_string "b"; a result (fun y -> (print_string "c"; n Example: x + y))))) in # let compose f g = fun x -> f (g x);; let u = (print_string "d"; 3) in val compose : ('a -> 'b) -> ('c -> 'a) -> 'c -> let g = f u in 'b = let v = (print_string "e"; 5) in g v;; n The type ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b is a higher order type because of adbec- : int = 8 ('a -> 'b) and ('c -> 'a) and -> 'c -> 'b

9/11/14 9 9/11/14 10

Thrice Thrice n Recall: n Recall: # let thrice f x = f (f (f x));; # let thrice f x = f (f (f x));; val thrice : ('a -> 'a) -> 'a -> 'a = val thrice : ('a -> 'a) -> 'a -> 'a = n How do you write thrice with compose? n How do you write thrice with compose? # let thrice f = compose f (compose f f);; val thrice : ('a -> 'a) -> 'a -> 'a =

n Is this the only way?

9/11/14 11 9/11/14 12 Partial Application Lambda Lifting

# (+);; n You must remember the rules for evaluation - : int -> int -> int = when you use partial application # (+) 2 3;; # let add_two = (+) (print_string "test\n"; 2);; - : int = 5 test # let plus_two = (+) 2;; val add_two : int -> int = val plus_two : int -> int = # plus_two 7;; # let add2 = (* lambda lifted *) - : int = 9 fun x -> (+) (print_string "test\n"; 2) x;; n Patial application also called sectioning val add2 : int -> int =

9/11/14 13 9/11/14 14

Lambda Lifting Partial Application and “Unknown Types”

# thrice add_two 5;; n Recall compose plus_two: - : int = 11 # let f1 = compose plus_two;; # thrice add2 5;; val f1 : ('_a -> int) -> '_a -> int = test test n Compare to lambda lifted version: test # let f2 = fun g -> compose plus_two g;; - : int = 11 val f2 : ('a -> int) -> 'a -> int = n Lambda lifting delayed the evaluation of the n What is the difference? argument to (+) until the second argument was supplied

9/11/14 15 9/11/14 16

Partial Application and “Unknown Types” Partial Application and “Unknown Types” n ‘_a can only be instantiated once for an expression n ‘a can be repeatedly instantiated # f1 plus_two;; - : int -> int = # f2 plus_two;; # f1 List.length;; - : int -> int = Characters 3-14: # f2 List.length;; f1 List.length;; - : '_a list -> int = ^^^^^^^^^^^ This expression has type 'a list -> int but is here used with type int -> int

9/11/14 17 9/11/14 18 Lists

n First example of a recursive datatype (aka Your turn now algebraic datatype)

n Unlike tuples, lists are homogeneous in Try Problem 2 on HW3 type (all elements same type)

9/11/14 19 9/11/14 20

Lists Lists

# let fib5 = [8;5;3;2;1;1];; n List can take one of two forms: val fib5 : int list = [8; 5; 3; 2; 1; 1] n Empty list, written [ ] # let fib6 = 13 :: fib5;; n Non-empty list, written x :: xs val fib6 : int list = [13; 8; 5; 3; 2; 1; 1] n x is head element, xs is tail list, :: called # (8::5::3::2::1::1::[ ]) = fib5;; “” - : bool = true n Syntactic sugar: [x] == x :: [ ] # fib5 @ fib6;;

n [ x1; x2; …; xn] == x1 :: x2 :: … :: xn :: [ ] - : int list = [8; 5; 3; 2; 1; 1; 13; 8; 5; 3; 2; 1; 1]

9/11/14 21 9/11/14 22

Lists are Homogeneous Question

# let bad_list = [1; 3.2; 7];; n Which one of these lists is invalid? Characters 19-22: let bad_list = [1; 3.2; 7];; 1. [2; 3; 4; 6] ^^^ 2. [2,3; 4,5; 6,7] This expression has type float but is here 3. [(2.3,4); (3.2,5); (6,7.2)] used with type int 4. [[“hi”; “there”]; [“wahcha”]; [ ]; [“doin”]]

9/11/14 23 9/11/14 24 Answer Functions Over Lists n Which one of these lists is invalid? # let rec double_up list = match list 1. [2; 3; 4; 6] with [ ] -> [ ] (* pattern before ->, expression after *) 2. [2,3; 4,5; 6,7] | (x :: xs) -> (x :: x :: double_up xs);; 3. [(2.3,4); (3.2,5); (6,7.2)] val double_up : 'a list -> 'a list = 4. [[“hi”; “there”]; [“wahcha”]; [ ]; [“doin”]] # let fib5_2 = double_up fib5;; val fib5_2 : int list = [8; 8; 5; 5; 3; 3; 2; 2; 1; § 3 is invalid because of last pair 1; 1; 1]

9/11/14 25 9/11/14 26

Functions Over Lists Question: Length of list

# let silly = double_up ["hi"; "there"];; n Problem: write code for the length of the list val silly : string list = ["hi"; "hi"; "there"; "there"] n How to start? # let rec poor_rev list = let rec length l = match list with [] -> [] | (x::xs) -> poor_rev xs @ [x];; val poor_rev : 'a list -> 'a list = # poor_rev silly;; - : string list = ["there"; "there"; "hi"; "hi"]

9/11/14 27 9/11/14 28

Question: Length of list Question: Length of list n Problem: write code for the length of the list n Problem: write code for the length of the list

n How to start? n What patterns should we match against? let rec length l = let rec length l = match l with match l with

9/11/14 29 9/11/14 30 Question: Length of list Question: Length of list n Problem: write code for the length of the list n Problem: write code for the length of the list

n What patterns should we match against? n What result do we give when l is empty? let rec length l = let rec length l = match l with [] -> match l with [] -> | (a :: bs) -> | (a :: bs) ->

9/11/14 31 9/11/14 32

Question: Length of list Question: Length of list n Problem: write code for the length of the list n Problem: write code for the length of the list

n What result do we give when l is empty? n What result do we give when l is not empty? let rec length l = let rec length l = match l with [] -> 0 match l with [] -> 0 | (a :: bs) -> | (a :: bs) ->

9/11/14 33 9/11/14 34

Question: Length of list n Problem: write code for the length of the list Your turn now n What result do we give when l is not empty? let rec length l = match l with [] -> 0 | (a :: bs) -> 1 + length bs Try Problem 1 on MP3

9/11/14 35 9/11/14 36 Same Length Same Length n How can we efficiently answer if two lists n How can we efficiently answer if two lists have the same length? have the same length? let rec same_length list1 list2 = match list1 with [] -> (match list2 with [] -> true | (y::ys) -> false) | (x::xs) -> (match list2 with [] -> false | (y::ys) -> same_length xs ys) 9/11/14 37 9/11/14 38

Structural Structural Recursion : List Example n Functions on recursive datatypes (eg lists) # let rec length list = match list tend to be recursive with [ ] -> 0 (* Nil case *) n Recursion over recursive datatypes generally | x :: xs -> 1 + length xs;; (* Cons case *) by structural recursion val length : 'a list -> int = n Recursive calls made to components of structure of the same recursive type # length [5; 4; 3; 2];;

n Base cases of recursive types stop the recursion - : int = 4 of the function n Nil case [ ] is base case

n Cons case recurses on component list xs

9/11/14 39 9/11/14 40

Forward Recursion Forward Recursion: Examples n In Structural Recursion, split input into components and (eventually) recurse # let rec double_up list = match list n Forward Recursion form of Structural with [ ] -> [ ] Recursion | (x :: xs) -> (x :: x :: double_up xs);; n In forward recursion, first call the function val double_up : 'a list -> 'a list =

recursively on all recursive components, and # let rec poor_rev list = then build final result from partial results match list n Wait until whole structure has been with [] -> [] traversed to start building answer | (x::xs) -> poor_rev xs @ [x];; val poor_rev : 'a list -> 'a list =

9/11/14 41 9/11/14 42 Forward Recursion: Example Question

# let rec map f list = n How do you write length with forward match list recursion? with [] -> [] let rec length l = | (h::t) -> (f h) :: (map f t);; val map : ('a -> 'b) -> 'a list -> 'b list = # map plus_two fib5;; - : int list = [10; 7; 5; 4; 3; 3] # map (fun x -> x - 1) fib6;; : int list = [12; 7; 4; 2; 1; 0; 0]

9/11/14 43 9/11/14 44

Question Question n How do you write length with forward n How do you write length with forward recursion? recursion? let rec length l = let rec length l = match l with [] -> match l with [] -> | (a :: bs) -> | (a :: bs) -> 1 + length bs

9/11/14 45 9/11/14 46

Question n How do you write length with forward recursion? Your turn now let rec length l = match l with [] -> 0 | (a :: bs) -> 1 + length bs Try Problem 8 on MP3

9/11/14 47 9/11/14 48 An Important Optimization An Important Optimization

n When a function call is made, n When a function call is made, Normal the return address needs to be Tail the return address needs to be call saved to the stack so we know call saved to the stack so we know to where to return when the to where to return when the h call is finished h call is finished g n What if f calls g and g calls h, f n What if f calls g and g calls h, f but calling h is the last thing g … but calling h is the last thing g does (a tail call)? does (a tail call)? … n Then h can return directly to f instead of g

9/11/14 49 9/11/14 50

Tail Recursion Example of Tail Recursion

# let rec prod l = n A recursive program is tail recursive if all recursive calls are tail calls match l with [] -> 1 | (x :: rem) -> x * prod rem;; n Tail recursive programs may be optimized to val prod : int list -> int = be implemented as loops, thus removing the # let prod list = let rec prod_aux l acc = function call overhead for the recursive calls match l with [] -> acc n Tail recursion generally requires extra | (y :: rest) -> prod_aux rest (acc * y) “accumulator” arguments to pass partial (* Uses associativity of multiplication *) results in prod_aux list 1;; val prod : int list -> int = n May require an auxiliary function

9/11/14 51 9/11/14 52

Question Question n How do you write length with tail recursion? n How do you write length with tail recursion? let length l = let length l = let rec length_aux list n =

in

9/11/14 53 9/11/14 54 Question Question n How do you write length with tail recursion? n How do you write length with tail recursion? let length l = let length l = let rec length_aux list n = let rec length_aux list n = match list with [] -> match list with [] -> n | (a :: bs) -> | (a :: bs) -> in in

9/11/14 55 9/11/14 56

Question Question n How do you write length with tail recursion? n How do you write length with tail recursion? let length l = let length l = let rec length_aux list n = let rec length_aux list n = match list with [] -> n match list with [] -> n | (a :: bs) -> length_aux | (a :: bs) -> length_aux bs in in

9/11/14 57 9/11/14 58

Question Question n How do you write length with tail recursion? n How do you write length with tail recursion? let length l = let length l = let rec length_aux list n = let rec length_aux list n = match list with [] -> n match list with [] -> n | (a :: bs) -> length_aux bs (n + 1) | (a :: bs) -> length_aux bs (n + 1) in in length_aux l 0

9/11/14 59 9/11/14 60 Mapping Functions Over Lists

# let rec map f list = Your turn now match list with [] -> [] | (h::t) -> (f h) :: (map f t);; val map : ('a -> 'b) -> 'a list -> 'b list = Try Problem 10 on MP3 # map plus_two fib5;; - : int list = [10; 7; 5; 4; 3; 3] # map (fun x -> x - 1) fib6;; : int list = [12; 7; 4; 2; 1; 0; 0]

9/11/14 61 9/11/14 62

Mapping Recursion Mapping Recursion n One common form of structural recursion n Can use the higher-order recursive map applies a function to each element in the function instead of direct recursion structure # let doubleList list = # let rec doubleList list = match list List.map (fun x -> 2 * x) list;; with [ ] -> [ ] val doubleList : int list -> int list = | x::xs -> 2 * x :: doubleList xs;; # doubleList [2;3;4];; val doubleList : int list -> int list = - : int list = [4; 6; 8] # doubleList [2;3;4];; - : int list = [4; 6; 8] n Same function, but no rec

9/11/14 63 9/11/14 64

Folding Recursion Folding Functions over Lists n Another common form “folds” an operation How are the following functions similar? over the elements of the structure # let rec sumlist list = match list with # let rec multList list = match list [ ] -> 0 | x::xs -> x + sumlist xs;; val sumlist : int list -> int = with [ ] -> 1 # sumlist [2;3;4];; | x::xs -> x * multList xs;; - : int = 9 val multList : int list -> int = # let rec prodlist list = match list with # multList [2;4;6];; [ ] -> 1 | x::xs -> x * prodlist xs;; val prodlist : int list -> int = - : int = 48 # prodlist [2;3;4];; n Computes (2 * (4 * (6 * 1))) - : int = 24

9/11/14 65 9/11/14 66 Iterating over lists Folding Recursion

# let rec fold_right f list b = n multList folds to the right match list n Same as: with [] -> b | (x :: xs) -> f x (fold_right f xs b);; # let multList list = val fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b = List.fold_right (fun x -> fun p -> x * p) # fold_right (fun s -> fun () -> print_string s) list 1;; ["hi"; "there"] val multList : int list -> int = ();; # multList [2;4;6];; therehi- : unit = () - : int = 48

9/11/14 67 9/11/14 68

Encoding Recursion with Fold Question

let rec length l = # let rec append list1 list2 = match list1 with [ ] -> list2 | x::xs -> x :: append xs list2;; match l with [] -> 0 val append : 'a list -> 'a list -> 'a list = | (a :: bs) -> 1 + length bs

Base Case Operation Recursive Call n How do you write length with fold_right, but no explicit recursion? # let append list1 list2 = fold_right (fun x y -> x :: y) list1 list2;; val append : 'a list -> 'a list -> 'a list = # append [1;2;3] [4;5;6];; - : int list = [1; 2; 3; 4; 5; 6]

9/11/14 69 9/11/14 70

Question Map from Fold let rec length l = # let map f list = match l with [] -> 0 fold_right (fun x -> fun y -> f x :: y) list | (a :: bs) -> 1 + length bs [ ];; n How do you write length with fold_right, but val map : ('a -> 'b) -> 'a list -> 'b list = no explicit recursion? let length list = # map ((+)1) [1;2;3];; List.fold_right (fun x -> fun n -> n + 1) list 0 - : int list = [2; 3; 4] n Can you write fold_right (or fold_left) with just map? How, or why not?

9/11/14 71 9/11/14 72 Iterating over lists Encoding Tail Recursion with fold_left

# let rec fold_left f a list = match list # let prod list = let rec prod_aux l acc = with [] -> a match l with [] -> acc | (x :: xs) -> fold_left f (f a x) xs;; | (y :: rest) -> prod_aux rest (acc * y) in prod_aux list 1;; val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a = val prod : int list -> int = # fold_left Init Acc Value Recursive Call Operation

(fun () -> print_string) # let prod list = () List.fold_left (fun acc y -> acc * y) 1 list;; ["hi"; "there"];; val prod: int list -> int = hithere- : unit = () # prod [4;5;6];; - : int =120

9/11/14 73 9/11/14 74

Question Question

let length l = let length l = let rec length_aux list n = let rec length_aux list n = match list with [] -> n match list with [] -> n | (a :: bs) -> length_aux bs (n + 1) | (a :: bs) -> length_aux bs (n + 1) in length_aux l 0 in length_aux l 0

n How do you write length with fold_left, but n How do you write length with fold_left, but no explicit recursion? no explicit recursion? let length list = List.fold_left (fun n -> fun x -> n + 1) 0 list 9/11/14 75 9/11/14 76

Folding Recall

# let rec fold_left f a list = match list # let rec poor_rev list = match list with [] -> a | (x :: xs) -> fold_left f (f a x) xs;; with [] -> [] val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a = | (x::xs) -> poor_rev xs @ [x];; val poor_rev : 'a list -> 'a list = fold_left f a [x1; x2;…;xn] = f(…(f (f a x1) x2)…)xn

# let rec fold_right f list b = match list with [ ] -> b | (x :: xs) -> f x (fold_right f xs b);; val fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b = n What is its running time? fold_right f [x1; x2;…;xn] b = f x1(f x2 (…(f xn b)…))

9/11/14 77 9/11/14 78 Quadratic Time Tail Recursion - Example n Each step of the recursion takes time proportional to input # let rec rev_aux list revlist = match list with [ ] -> revlist n Each step of the recursion makes only one recursive call. | x :: xs -> rev_aux xs (x::revlist);; n List example: val rev_aux : 'a list -> 'a list -> 'a list =

# let rec poor_rev list = match list # let rev list = rev_aux list [ ];; with [] -> [] val rev : 'a list -> 'a list = | (x::xs) -> poor_rev xs @ [x];; val poor_rev : 'a list -> 'a list = n What is its running time?

9/11/14 79 9/11/14 80

Comparison Comparison n poor_rev [1,2,3] = n rev [1,2,3] = n (poor_rev [2,3]) @ [1] = n rev_aux [1,2,3] [ ] = n ((poor_rev [3]) @ [2]) @ [1] = n rev_aux [2,3] [1] = n (((poor_rev [ ]) @ [3]) @ [2]) @ [1] = n (([ ] @ [3]) @ [2]) @ [1]) = n rev_aux [3] [2,1] = n ([3] @ [2]) @ [1] = n rev_aux [ ] [3,2,1] = [3,2,1] n (3:: ([ ] @ [2])) @ [1] = n [3,2] @ [1] = n 3 :: ([2] @ [1]) = n 3 :: (2:: ([ ] @ [1])) = [3, 2, 1]

9/11/14 81 9/11/14 82

Folding - Tail Recursion Folding

- # let rev list = n Can replace recursion by fold_right in any - fold_left forward primitive recursive definition

- (fun l -> fun x -> x :: l) //comb op n Primitive recursive means it only recurses on [] //accumulator cell immediate subcomponents of recursive data structure list n Can replace recursion by fold_left in any tail primitive recursive definition

9/11/14 83 9/11/14 84