Evaluating declarations
Programming Languages and n Evaluation uses an environment ρ Compilers (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, c = <(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 =
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 =
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 =
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 =
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 =
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;; “cons” - : 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 =
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 =
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 Recursion 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 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 =
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 =
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 =
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 =
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 =
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
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 =
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 =
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?
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 =
(fun () -> print_string) # let prod list = () List.fold_left (fun acc y -> acc * y) 1 list;; ["hi"; "there"];; val prod: int list -> int =
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];;
# 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 =
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 =
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