310 likes | 422 Views
06 List Processing. Version 2.0. Predefined Polymorphic Lists. # [] ;; (+ Nil +) - : 'a list = [] # 1::[] ;; (+ Cons +) - : int list = [1] # true::[];; - : bool list = [true] # 1::2::3::4::5::[] ;;
E N D
06List Processing Version 2.0
Predefined Polymorphic Lists # [] ;; (+ Nil +) - : 'a list = [] # 1::[] ;; (+ Cons +) - : int list = [1] # true::[];; - : bool list = [true] # 1::2::3::4::5::[] ;; - : int list = [1; 2; 3; 4; 5] # [1;2;3;4;5] ;; - : int list = [1; 2; 3; 4; 5] # 1::2::[3;4;5] ;; - : int list = [1; 2; 3; 4; 5] # List.hd [1;2;3;4;5] ;;(+ head +) - : int = 1 # List.tl [1;2;3;4;5] ;;(+ tail +) - : int list = [2; 3; 4; 5] # [1;2;3]@[4;5] ;;(+ append +) - : int list = [1; 2; 3; 4; 5]
Definition: Lists in OCaml • Structural Inductive Definition of Lists of type ‘a • (basis) An empty list, [], is a list of type ‘a. • (induction) If xs is a list of values of type ‘a and x is a value of type ‘a, then so is x::xs. • Nothing else is a list. • Structural Induction • is P([]) true? • Assume P(xs) is true, is P(x::xs) true? • If we can say “yes” to these two questions, then we can assure that P(xs) is true for all lists xs.
Computing List Length # let rec length xs = match xs with | [] -> 0 | _::xs -> 1 + length xs ;; val length : 'a list -> int = <fun> # length [3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3] ;; - : int = 18 # length [] ;; - : int = 0 Primitive # let length xs = let rec loop xs n = match xs with | [] -> n | _::xs -> loop xs (n+1) in loop xs 0 ;; val length : 'a list -> int = <fun> Tail
Computation Trace (Primitive list length) length [1;2;3;4;5] • 1 + length [2;3;4;5] • 1 + (1 + length [3;4;5]) • 1 + (1 + (1 + length [4;5])) • 1 + (1 + (1 + (1 + length [5]))) • 1 + (1 + (1 + (1 + (1 + length [])))) • 1 + (1 + (1 + (1 + (1 + 0)))) • 1 + (1 + (1 + (1 + 1))) • 1 + (1 + (1 + 2)) • 1 + (1 + 3) • 1 + 4 • 5 let rec length xs = match xs with | [] -> 0 | _::xs -> 1 + length xs • Complexity • - time: θ(n) • space: θ(n)
Computation Trace (Tail list length) length [1;2;3;4;5] • loop [1;2;3;4;5] 0 • loop [2;3;4;5] 1 • loop [3;4;5] 2 • loop [4;5] 3 • loop [5] 4 • loop [] 5 • 5 let length xs = let rec loop xs n = match xs with | [] -> n | _::xs -> loop xs (n+1) in loop xs 0 • Complexity • - time: θ(n) • space: θ(1)
Verification (Primitive list length) • Theorem : The primitive recursive function call length xs evaluates to the length of xs, |xs|, for all lists xs. let rec length xs = match xs with | [] -> 0 | _::xs -> 1 + length xs • Proof: We prove by structural induction on xs. (Basis) xs = [], length [] = 0 by definition of program ‘length’ = |[]| by inspection (Induction hypothesis) For some list xs of length ≥ 0, length xs evaluates to |xs| (Induction step) It suffices to show that length (x::xs) evaluates to |x::xs| length (x::xs) = 1 + length xs by definition of program length = 1 + |xs| by induction hypothesis = |x::xs| by inspection
Verification (Tail list length) • Theorem : The tail recursive function call length xs evaluates to the length of xs, |xs|, for all lists xs. let length xs = let rec loop xs n = match xs with | [] -> n | _::xs -> loop xs (n+1) in loop xs 0 • Proof: We first show that loop xs n evaluates to n+|xs| by structural induction on xs. (Basis) xs = [], loop [] n = n by definition of program ‘loop’ = n + 0 by algebra = n + |[]| by inspection (Induction hypothesis) For some list xs of length ≥ 0, loop xs n evaluates to n+|xs| (Induction step) It suffices to show that loop (x::xs) n evaluates to n+|x::xs| loop (x::xs) n = loop xs (n+1) by definition of program loop = n+1+|xs| by induction hypothesis = n+|x::xs| by inspection Thus length xs = loop xs 0 evaluates to 0+|xs| = |xs|.
Computing List Append (* Concatenate two lists next to each other *) # let rec append xs ys = match xs with | [] -> ys | x::xs -> x::append xs ys ;; val append : 'a list -> 'a list -> 'a list = <fun> Primitive @ is the infix version of this append function Execution Trace append [1;2;3] [4;5] • 1 :: append [2;3] [4;5] • 1 :: 2 :: append [3] [4;5] • 1 :: 2 :: 3 :: append [] [4;5] • 1 :: 2 :: 3 :: [4;5] • 1 :: 2 :: [3;4;5] • 1 :: [2;3;4;5] • [1;2;3;4;5] • Complexity • - time: θ(n) • space: θ(n)
Verification (Primitive list append) • Theorem : The primitive recursive function call append xs ys evaluates to the concatenation of lists xs and ys, xs@ys, for all lists xs and ys. let rec append xs ys = match xs with | [] -> ys | x::xs -> x::append xs ys • Proof: We prove by structural induction on xs. (Basis) xs = [], append [] ys = ys by definition of program ‘append’ = []@ys by property of concatenation (Induction hypothesis) For some list xs of length ≥ 0, append xs ys evaluates to xs@ys (Induction step) It suffices to show that append (x::xs) ys evaluates to (x::xs)@ys append (x::xs) ys = x :: append xs ys by definition of program ‘append’ = x :: (xs@ys) by induction hypothesis = (x::xs)@ys by property of :: and concatenation
Computing List Reverse (* Rearrange a list in reverse order*) # let rec rev xs = match xs with | [] -> [] | x::xs -> rev xs @ [x] ;; val rev : 'a list -> 'a list = <fun> Primitive Execution Trace rev [1;2;3;4] • rev [2;3;4] @ [1] • rev [3;4] @ [2] @ [1] • rev [4] @ [3] @ [2] @ [1] • rev[] @ [4] @ [3] @ [2] @ [1] • [] @ [4] @ [3] @ [2] @ [1] • [4] @ [3] @ [2] @ [1] • [4;3] @ [2] @ [1] • [4;3;2] @ [1] • [4;3;2;1] • Complexity • - time: θ(n2) • space: θ(n)
Verification (Primitive list reverse) • Theorem : The primitive recursive function call reverse xs evaluates to the reverse of xs, ~xs, for all lists xs. let rec rev xs = match xs with | [] -> [] | x::xs -> rev xs @ [x] • Proof: We prove by structural induction on xs. (Basis) xs = [], rev [] = [] by definition of program ‘rev’ = ~[] by inspection (Induction hypothesis) For some list xs of length ≥ 0, rev xs evaluates to ~xs (Induction step) It suffices to show that rev (x::xs) evaluates to ~(x::xs) rev (x::xs) = rev xs @ [x] by definition of program ‘rev’ = ~xs @ [x] by induction hypothesis = ~(x::xs) by inspection
Computing List Reverse (* Rearrange a list in reverse order*) # let rev xs = let rec loop xs ys = match xs with | [] -> ys | x::xs -> loop xs (x::ys) in loop xs [] ;; val rev : 'a list -> 'a list = <fun> Tail Execution Trace rev [1;2;3;4] • loop [1;2;3;4] [] • loop [2;3;4] [1] • loop [3;4] [2;1] • loop [4] [3;2;1] • loop [] [4;3;2;1] • [4;3;2;1] • Complexity • - time: θ(n) • space: θ(1)
Verification (Tail list reverse) • Theorem : The tail recursive function call rev xs evaluates to the reverse of xs, ~xs, for all lists xs. let rev xs = let rec loop xs ys = match xs with | [] -> ys | x::xs -> loop xs (x::ys) in loop xs [] • Proof: We first show that loop xs ys evaluates to ~xs@ys by structural induction on xs. (Basis) xs = [], loop [] ys = ys by definition of program ‘loop’ = []@ys by property of @ = ~[]@ys by inspection (Induction hypothesis) For some list xs of length ≥ 0, loop xs ys evaluates to ~xs@ys (Induction step) It suffices to show that loop (x::xs) ys evaluates to ~(x::xs)@ys loop (x::xs) ys = loop xs (x::ys) by definition of program ‘loop’ = ~xs@(x::ys) by induction hypothesis = ~xs@[x]@ys = ~(x::xs)@ys by inspection Thus rev xs = loop xs [] evaluates to ~xs@[] = ~xs
Computing List Append # let append xs ys = let rec loop xs ys = match xs with | [] -> ys | x::xs -> loop xs (x::ys) in loop (rev xs) ys ;; val append : 'a list -> 'a list -> 'a list = <fun> Tail Execution Trace append [1;2;3] [4;5] • loop (rev [1;2;3]) [4;5] • loop (loop [1;2;3] []) [4;5] • loop (loop [2;3] [1]) [4;5] • loop (loop [3] [2;1]) [4;5] • loop (loop [] [3;2;1]) [4;5] • loop [3;2;1] [4;5] • loop [2;1] [3;4;5] • loop [1] [2;3;4;5] • loop [] [1;2;3;4;5] • [1;2;3;4;5] • Complexity • - time: θ(n) • space: θ(1)
Verification (Tail list append) • Theorem : The tail recursive function call append xs ys evaluates to the concatenation of xs and ys, xs@ys, for all lists xs and ys. let append xs ys = let rec loop xs ys = match xs with | [] -> ys | x::xs -> loop xs (x::ys) in loop (rev xs) ys • Proof: The ‘loop’ function here is identical to the ‘loop’ function in the tail recursive version of ‘rev’. Thus we already proved that loop xs ys evaluates to ~xs@ys [1] . append xs ys = loop (rev xs) ys by definition of ‘append’ = loop ~xs ys by previous theorem = ~(~xs)@ys by [1] = xs@ys by property of reverse
Sorting • sort : ‘a list -> ‘a list • input: the list of values of type • output: the rearranged list of values in ascending order.
Selection Sort • Algorithm • Initialization • unsorted list = input list • sorted list = empty • Find the minimum value in the unsorted list. • Move it to the back of the sorted list. • Repeat the steps 2~3 until the unsorted list is empty.
Program Selection Sort (* find_min : ‘a list -> ‘a * ‘a list *) let rec find_min xs = match xs with | y::[] -> (y,[]) | y::ys -> let (m,zs) = find_min ys in if y<m then (y,ys) else (m,y::zs) | [] -> failwith "This message will never be displayed" (* selection_sort : 'a list -> 'a list *) let rec selection_sort xs = match xs with | [] -> [] | _ -> let (m,ys) = find_min xs in m :: selection_sort ys
Insertion Sort • Algorithm • Initialization • unsorted list = input list • sorted list = empty • Remove a value from the unsorted list. • Insert it at the correct position in the sorted list. • Repeat the steps 2~3 until the unsorted list is empty.
Program Insertion Sort (* insert : 'a list -> 'a -> 'a list *) let rec insert xs x = match xs with | [] -> [x] | y::ys -> if y<x then y :: insert ys x else x :: xs (* insertion_sort : 'a list -> 'a list *) let insertion_sort xs = let rec loop sorted unsorted = match unsorted with | [] -> sorted | y::ys -> loop (insert sorted y) ys in loop [] xs
Merge Sort • Algorithm • If the list is of length 0 or 1, then it is already sorted. • Otherwise: • Split the unsorted list into two sublists of about half the size. • Sort each sublist recursively by re-applying merge sort. • Merge the two sorted sublist back into one sorted list.
Program Merge Sort ~ (* split : 'a list -> 'a list * 'a list *) let rec split xs = match xs with | [] -> ([],[]) | [x] -> ([x],[]) | x::xs' -> let (ls,rs) = split xs' in if List.length ls <= List.length rs then (x::ls,rs) else (ls,x::rs) (* merge : 'a list -> 'a list -> 'a list *) let rec merge xs ys = match (xs,ys) with | ([],_) -> ys | (_,[]) -> xs | (x::xs',y::ys') -> if x<y then x :: merge xs' ys else y :: merge xs ys'
Program Merge Sort (* merge_sort : 'a list -> 'a list *) let rec merge_sort xs = match xs with | [] -> [] | [x] -> [x] | _ -> let (ls,rs) = split xs in merge (merge_sort ls) (merge_sort rs)
Quicksort • Algorithm • Pick an element, called a pivot, from the list. • [Partition] Reorder the list • so that all elements which are less than the pivot come before the pivot and • so that all elements greater than the pivot come after it • Recursively sort the sublist of lesser elements and the sublist of greater elements.
Program Quicksort (* partition : 'a -> 'a list -> 'a list * 'a list *) let partition pivot xs = (List.filter (fun x -> x < pivot) xs, List.filter (fun x -> pivot <= x) xs) (* quicksort : 'a list -> 'a list *) let rec quicksort xs = match xs with | [] -> [] | pivot::xs' -> let (ls,rs) = partition pivot xs' in (quicksort ls) @ [pivot] @ (quicksort rs)