let rec even x = match x with | 0 -> true | 1 -> false | _ -> not (even (x - 1)) The last action of even: "Examine the result of the recursive call. Apply not to it. Return myself." This last action is called the "continuation". even 4 ==> match 4 with | 0 -> true | 1 -> false | _ -> not (even (4 - 1)) ==> not (even 3) ==> not (match 3 with | 0 -> true | 1 -> false | _ -> not (even (3 - 1))) ==> not (not (even 2)) ==> not (not (match 2 with | 0 -> true | 1 -> false | _ -> not (even (2 - 1)))) ==> not (not (not (even 1))) ==> not (not (not false)) ==> not (not true) ==> not false ==> true even n ==>* not (not (not (not (not (... (not false) ...))))) <=== Recursive stack is of size linear in the input. ==>* true | false Ocaml recursion stack is transated into the recusion stack of the machine: push pop, stackpointer Machine recusion stack has limited capacity. even 9332812 will cause the stack to overflow === let rec even2 x = match x with | 0 -> true | 1 -> false | _ -> even2 (x - 2);; even2 4 ==> match 4 with ... | (even2 2) | match 2 with ... | ((even2 0)) | true Ocaml language standards mandates the use of tail call optimization. Tail call: When the last action of a function is to make a recursive call to itself. === Question: Can we transform the original even into something that works with big numbers? Observation: Even though machine stack space (1000s of function calls) is limited, the heap space (32GB) is not. Homework exercise: How deep is your computer's function stack? let rec even3 x = let rec _even3 stack x = match x with | 0 -> true | 1 -> false | _even3 ("not" :: stack) (x - 1) in _even3 [] x let rec collapse_stack stack = match stack with | hd :: [] -> hd = "true" | hd1 :: (_ :: tl) -> collapse_stack (if hd1 = "true" then "false" :: tl else "true" :: tl);; let rec _even3 stack x = match x with | 0 -> collapse_stack ("true" :: stack) | 1 -> collapse_stack ("false" :: stack) | _ -> _even3 ("not" :: stack) (x - 1));; stack ::= "true" :: "not" :: "not" :: "not" :: ... | "false" :: "not" :: "not" :: "not" :: ... | "not" :: "not" :: "not" :: ... "____Continuation______ passing style" (CPS) :: All about playing with the call stack. "First-class call stacks". C / C++ standard libary calls: set_jmp, long_jmp allow continuation passing Scheme: call/cc "call with current continuation" ======================================================================================================================== let t = 5 in t "Start looking outwards from hte variable name Find the first place it was declared." let t = 3 in (let t = 2 in t) let t = 3 in (let u = 5 in t) let foo l = (3 :: l);; let l = [ 1; 2; 3];; let foo l = (3 :: l);; foo [4; 5];; (* Prediction: 3, 4, 5 *) let foo x = let bar x = (x + 1) in (bar 5) foo 2 (* If nested x refers to outermost x, then 3 *) (* If nested x refers to argument to bar, then 6 *) - : int = 6 Question: Rename argument name of foo from x to y. let foo y = let bar x = (x + 1) in (bar 5) Question: Rename argument name of bar from x to y. let foo x = let bar y = (y + 1) in (bar 5) Experiment: What if let foo x = let bar y = (x + 1) in (bar 5) let l = [ 1; 2; 3];; let rec foo l = match l with | [] -> 0 | hd :: l -> 1 + foo l;; let cube x = x * x * x = x ** 3.;; let fourth x = x * x * x * x = x ** 4.; let fifth x = x * x * x * x * x = x ** 5.;; let nth n be that function which returns the nth power. let foo x = x + 1; let y = foo 3;; ==> 3 + 1;; ( Function inlining ) let f x y = x + y - 3 let g x = f x 0 (x + 0 - 3) (x - 3) (When do partial evaluation, arithmetic optimization, x * 1 ==> x ) let andThen f g = let h x = g (f x) in h What is the type of andThen? - Generic types - (Two functions as input arguments). One function as output argument. - Function -> Function -> Function - ('a -> 'b) -> ('b -> 'c) -> ('a -> 'c) let andThen f g = let h x = g (f x) in h