Lecture 004

non-negative int lists code step
0 [] base case base case
n > 0 x :: xs inductive / recursive inductive case

Reason Through Recursive Functions

Slow

(* length: int list -> int
REQUIRES: true
ENSURES:
*)
fun length ([] : int list): int = 0
  | length (x :: xs) = 1 + length xs

This will result adding 1 AFTER recursive call finishes

Tail Recursive

(* tlength: (int list * int) -> int
REQUIRES: true
ENSURES: tlength (L, acc) ~= (length L ) + acc
*)
fun tlength ([] : int list, acc : int) = 0
  | tlength (x::xs, acc) = tlength (xs, 1 + acc)

fun leng (L : int list) = tlength (L, 0)

Since the above function uses tail call (meaning that no work after the recursive call, the final recursive call returns the answer), it conserves space Tail Recursive: every recursive call is a trail call

Useful Assumption for Induction

Come To Extensional Equivalence

If e1 \Rightarrow e2, then e1 \simeq e2 If e1 \hookrightarrow v2, then e1 \simeq e2 If e1 \Rightarrow e and e2 \Rightarrow e, then e1 \simeq e2

(Note that e1 \Rightarrow e or e2 \Rightarrow e may not be true even with e1 \simeq e2)

Function Simplification: [3/y, 5/2] (fn (x : int) x + y + z) ~= (fn (x:int) => x + 8)

Using Totality

If f is total: f 1 + f 2 \simeq f 2 + f 1

Any exception + value is exception, therefore function are not associative in programming

total: for all expression applied to a function, the function reduce to a value valuable: expression reduce to a value without side-effects

Using Induction

What to induct:

(* append: int list * int list -> int list
example: append ([1, 2], [3, 4]) => [1, 2, 3, 4]
ruuning time: O(length A)
*)
fun append ([]: int list, B: int list) : int list = B
  | append (x:xs, B) = x :: append (xs, B)

This is built in append function that is right associative (infix form = @), meaning everything in the left will be appended one by one to the right

(* rev: int list -> int list
*)
fun rev ([]: int list): int list = []
  | rev (x :: xs) = rev xs @ [x]

This reverse function is bad because it has run time O(n^2)

(* trev: (int list * int list) -> int list
REQUIRES: true
ENSURES: trev (L, acc) ~= rev L @ acc
*)
fun trev ([]: int list, acc : int list): int list = acc
  | trev (x::xs, acc) = trev (xs, x::acc)

Theorem: trev (L, acc) ~= (rev L) @ acc (for all values L, valuable acc) Proof: by structural induction on L Base Case: L = [] WTS trev ([], acc) ~= rev [] @ acc trev ([], acc) ~= acc [1st clause of trev] ~= [] @ acc [append] ~= rev [] @ acc [1st clause of rev] TODO: see induction step on Piazza

TODO: is there a difference between value and valuable? (coding level difference? proof level difference?)

Table of Content