15-150 Fall 2020 Lecture 6 Stephen Brookes Most of the time I don't - - PowerPoint PPT Presentation

15 150 fall 2020 lecture 6
SMART_READER_LITE
LIVE PREVIEW

15-150 Fall 2020 Lecture 6 Stephen Brookes Most of the time I don't - - PowerPoint PPT Presentation

15-150 Fall 2020 Lecture 6 Stephen Brookes Most of the time I don't have much fun . The rest of the time I don't have any fun at all. today Sorting an integer list Using specifications to guide program design Helper functions should


slide-1
SLIDE 1

15-150 Fall 2020 Lecture 6

Stephen Brookes

Most of the time I don't have much fun. The rest of the time I don't have any fun at all.

slide-2
SLIDE 2

today

Sorting an integer list

  • Using specifications

to guide program design

  • “Helper functions should help!”

datatype definitions boolean connectives case expressions <> means ≠

SML features

. . . .

slide-3
SLIDE 3

datatypes

  • ML has datatype declarations
  • Allow us to introduce new types,

with constructors for building values datatype order = LESS | EQUAL | GREATER datatype ’a option = NONE | SOME of ’a NONE : int option SOME 42 : int option ’a list is a built-in datatype with constructors nil and ::

slide-4
SLIDE 4

comparing ints

datatype order = LESS | EQUAL | GREATER

a datatype

definition introducing the type

  • rder

with values LESS, EQUAL, GREATER

slide-5
SLIDE 5

comparing ints

datatype order = LESS | EQUAL | GREATER

a datatype

definition introducing the type

  • rder

with values LESS, EQUAL, GREATER

fun compare(x:int, y:int):order = if x<y then LESS else if y<x then GREATER else EQUAL

slide-6
SLIDE 6

comparing ints

datatype order = LESS | EQUAL | GREATER compare : int * int -> order compare(x,y) = LESS if x<y compare(x,y) = EQUAL if x=y compare(x,y) = GREATER if x>y

a datatype

definition introducing the type

  • rder

with values LESS, EQUAL, GREATER

fun compare(x:int, y:int):order = if x<y then LESS else if y<x then GREATER else EQUAL

slide-7
SLIDE 7

properties

  • ≤ is a linear ordering
  • < is defined by

and satisfies

If a ≤ b and b ≤ a then a = b (antisymmetric) If a ≤ b and b ≤ c then a ≤ c (transitive) Either a ≤ b or b ≤ a (connected) a < b if and only if (a ≤ b and a ≠ b) a < b or b < a or a = b (trichotomy)

  • f < and ≤ on integers
slide-8
SLIDE 8

sorted

A list is <-sorted (or just sorted) if and only if each item in the list is ≤ all later items.

sorted : int list -> bool fun sorted [ ] = true | sorted [x] = true | sorted (x::y::L) = (x <= y) andalso sorted(y::L)

slide-9
SLIDE 9

sorted

A list is <-sorted (or just sorted) if and only if each item in the list is ≤ all later items.

sorted : int list -> bool

For all L : int list, sorted(L) = true if L is sorted = false otherwise

fun sorted [ ] = true | sorted [x] = true | sorted (x::y::L) = (x <= y) andalso sorted(y::L)

slide-10
SLIDE 10

sorted

A list is <-sorted (or just sorted) if and only if each item in the list is ≤ all later items.

sorted : int list -> bool

For all L : int list, sorted(L) = true if L is sorted = false otherwise

fun sorted [ ] = true | sorted [x] = true | sorted (x::y::L) = (x <= y) andalso sorted(y::L)

(Prove this, by induction on list length) (Note the relevance of transitivity etc.)

slide-11
SLIDE 11

specs and code

  • We use sorted only in specifications.
  • Our sorting functions won’t use it.
  • But you could use it for testing...
slide-12
SLIDE 12

specs and code

  • We use sorted only in specifications.
  • Our sorting functions won’t use it.
  • But you could use it for testing...

For every integer list L there is a unique sorted permutation of L

slide-13
SLIDE 13

insertion sort

Insertion sort is a simple sorting algorithm that builds the sorted list recursively, one item at a time.

  • If the list is empty, do nothing.
  • Otherwise, each recursive call inserts an item from

the input list into its correct position in the sorted list so far.

slide-14
SLIDE 14

insertion sort

Insertion sort is a simple sorting algorithm that builds the sorted list recursively, one item at a time.

  • If the list is empty, do nothing.
  • Otherwise, each recursive call inserts an item from

the input list into its correct position in the sorted list so far.

(Wikipedia doesn’t give good specs!)

slide-15
SLIDE 15

insertion sort

  • If the list is empty, do nothing.
  • Otherwise, recursively sort the tail, then

insert the head item into its correct position in the (already sorted) tail.

slide-16
SLIDE 16

insertion sort

  • If the list is empty, do nothing.
  • Otherwise, recursively sort the tail, then

insert the head item into its correct position in the (already sorted) tail.

… We need a helper function

slide-17
SLIDE 17

insertion sort

  • If the list is empty, do nothing.
  • Otherwise, recursively sort the tail, then

insert the head item into its correct position in the (already sorted) tail.

… We need a helper function ins : int * int list -> int list REQUIRES … ENSURES …

slide-18
SLIDE 18

insertion

ins : int * int list -> int list

REQUIRES L is a sorted list ENSURES ins(x, L) = a sorted permutation of x::L

per·mu·ta·tion

noun
 A way, in which a list of things can be arranged: 
 "his thoughts raced ahead to fifty different permutations of what he must do"

Powered by Oxford Dictionaries

inserts x into its correct position in L

slide-19
SLIDE 19

insertion

ins : int * int list -> int list

REQUIRES L is a sorted list ENSURES ins(x, L) = a sorted permutation of x::L

fun ins (x, [ ]) = [x]

per·mu·ta·tion

noun
 A way, in which a list of things can be arranged: 
 "his thoughts raced ahead to fifty different permutations of what he must do"

Powered by Oxford Dictionaries

inserts x into its correct position in L

slide-20
SLIDE 20

insertion

ins : int * int list -> int list

REQUIRES L is a sorted list ENSURES ins(x, L) = a sorted permutation of x::L

fun ins (x, [ ]) = [x] | ins (x, y::R) =

per·mu·ta·tion

noun
 A way, in which a list of things can be arranged: 
 "his thoughts raced ahead to fifty different permutations of what he must do"

Powered by Oxford Dictionaries

inserts x into its correct position in L

slide-21
SLIDE 21

insertion

ins : int * int list -> int list

REQUIRES L is a sorted list ENSURES ins(x, L) = a sorted permutation of x::L

fun ins (x, [ ]) = [x] | ins (x, y::R) = if x > y

per·mu·ta·tion

noun
 A way, in which a list of things can be arranged: 
 "his thoughts raced ahead to fifty different permutations of what he must do"

Powered by Oxford Dictionaries

inserts x into its correct position in L

slide-22
SLIDE 22

insertion

ins : int * int list -> int list

REQUIRES L is a sorted list ENSURES ins(x, L) = a sorted permutation of x::L

fun ins (x, [ ]) = [x] | ins (x, y::R) = if x > y then y :: ins(x, R)

per·mu·ta·tion

noun
 A way, in which a list of things can be arranged: 
 "his thoughts raced ahead to fifty different permutations of what he must do"

Powered by Oxford Dictionaries

inserts x into its correct position in L

slide-23
SLIDE 23

insertion

else x :: (y :: R) ins : int * int list -> int list

REQUIRES L is a sorted list ENSURES ins(x, L) = a sorted permutation of x::L

fun ins (x, [ ]) = [x] | ins (x, y::R) = if x > y then y :: ins(x, R)

per·mu·ta·tion

noun
 A way, in which a list of things can be arranged: 
 "his thoughts raced ahead to fifty different permutations of what he must do"

Powered by Oxford Dictionaries

inserts x into its correct position in L

slide-24
SLIDE 24

ins equations

ins (x, [ ]) = [x] ins (x, y::R) = if x > y then y::ins(x, R) else x::(y::R)

For all values x, y : int and R : int list,

ins (x, y::R) = y::ins(x, R) if x > y = x::(y::R) otherwise

slide-25
SLIDE 25

Proof: By induction on length of L.

  • Base case: When L has length 0, L is [ ].

[ ] is sorted, and ins(x, [ ]) = [x] is a sorted perm of x::[ ].

  • Inductive case: Let k>0 and L be sorted, of length k.

Let y, R be the head, tail of L: so L = y::R. R is sorted, of length < k, and y ≤ all of R. Need to show: ins(x, y::R) = a sorted perm of x::(y::R)

For all sorted integer lists L, all values x:int, ins(x, L) = a sorted permutation of x::L.

IH: For all sorted lists A of length < k, all values x, ins(x, A) = a sorted perm of x::A.

correctness

slide-26
SLIDE 26

inductive case

R is sorted, length < k, and y ≤ all of R.

  • By IH, ins(x, R) = a sorted perm of x::R

If x>y we have ins(x, y::R) = y::ins(x,R) This list is sorted because... This list is a perm of x::(y::R) because... Otherwise, x≤y and ins(x, y::R) = x::(y::R) This list is sorted because... This list is a perm of x::(y::R) because...

  • In all cases, ins(x, y::R) = a sorted perm of x::(y::R)

ins (x, y::R) = = x::(y::R) otherwise, i.e. if x ≤ y y::ins(x, R) if x > y

(some more details)

slide-27
SLIDE 27

comments

  • Fill in the missing details in that proof sketch
  • Notice where you use basic properties of ≤
  • these properties are crucial
  • often used implicitly, without mention
  • that’s OK, except that you need to realize it

Now that we have ins, let’s define isort…

slide-28
SLIDE 28

isort

isort : int list -> int list

ENSURES isort(L) = a sorted perm of L

slide-29
SLIDE 29

isort

isort : int list -> int list

fun isort [ ] = [ ]

ENSURES isort(L) = a sorted perm of L

slide-30
SLIDE 30

isort

| isort (x::R) = ins (x, isort R)

isort : int list -> int list

fun isort [ ] = [ ]

ENSURES isort(L) = a sorted perm of L

slide-31
SLIDE 31

isort

| isort (x::R) = ins (x, isort R)

isort : int list -> int list

fun isort [ ] = [ ]

ENSURES isort(L) = a sorted perm of L

“isort (x::R) inserts x into its correct position in the sorted tail, isort R”

slide-32
SLIDE 32

Proof: By structural induction on L.

  • Base case: for L = [ ].

Show that isort [ ] = a sorted perm of [ ].

  • Inductive case: for L = y::R.

IH: isort R = a sorted perm of R. Show: isort(y::R) = a sorted perm of y::R.

For all values L: int list, isort L = a sorted permutation of L.

By the proven ins spec, it follows that ins (y, isort R) = a sorted perm of y::R

correctness

isort (y::R) = ins (y, isort R) isort R is a sorted perm of R

slide-33
SLIDE 33

comments

  • The proof was “by structural induction on L”
  • Every list value L is either [ ] (nil)
  • r y::R, where R is a “smaller” list value
  • We could just as well have said

“by induction on length of L”

  • [ ] has length 0
  • 0 ≤ length R < length(y::R)

isort (y::R) calls isort R

slide-34
SLIDE 34

perm facts

A perm of a perm of L is a perm of L In the correctness proof we used some obvious facts about permutations. y::(a perm of R) is a perm of (y::R)

slide-35
SLIDE 35

corollaries

slide-36
SLIDE 36

corollaries

isort is a total function from int list to int list

slide-37
SLIDE 37

corollaries

isort is a total function from int list to int list When e evaluates to L, isort e evaluates to the sorted version of L

slide-38
SLIDE 38

a variation

fun isort’ [ ] = [ ] | isort’ [x] = [x] | isort’ (x::R) = ins (x, isort’ R)

| isort (x::R) = ins (x, isort R)

fun isort [ ] = [ ]

slide-39
SLIDE 39

a variation

fun isort’ [ ] = [ ] | isort’ [x] = [x] | isort’ (x::R) = ins (x, isort’ R)

is this clause redundant

| isort (x::R) = ins (x, isort R)

fun isort [ ] = [ ]

slide-40
SLIDE 40

If in doubt, test, then prove

variation

fun isort’ [ ] = [ ] | isort’ [x] = [x] | isort’ (x::R) = ins (x, isort’ R)

isort’ : int list -> int list

slide-41
SLIDE 41

If in doubt, test, then prove

variation

fun isort’ [ ] = [ ] | isort’ [x] = [x] | isort’ (x::R) = ins (x, isort’ R)

isort’ : int list -> int list

slide-42
SLIDE 42

If in doubt, test, then prove

variation

fun isort’ [ ] = [ ] | isort’ [x] = [x] | isort’ (x::R) = ins (x, isort’ R)

isort’ : int list -> int list

slide-43
SLIDE 43

equivalent

  • isort and isort’ are extensionally equivalent:

For all L : int list, isort L = isort’ L.

  • Proof? See lecture notes…

OR: Re-do the isort proof for isort’ (easy)

Hence they satisfy the same spec, so

For all L : int list, isort L = isort’ L = the sorted perm of L

slide-44
SLIDE 44

equivalent

  • isort and isort’ are extensionally equivalent:

For all L : int list, isort L = isort’ L.

  • Proof? See lecture notes…

No need for extra clause but it doesn’t do any harm

OR: Re-do the isort proof for isort’ (easy)

Hence they satisfy the same spec, so

For all L : int list, isort L = isort’ L = the sorted perm of L

slide-45
SLIDE 45

work

  • Let Wins(n) be the work for ins(x, L)

when x, L are values and L has length n

  • Let Wisort(n) be the work for isort(L)

when L is a list of length n

slide-46
SLIDE 46

work

  • Let Wins(n) be the work for ins(x, L)

when x, L are values and L has length n

  • Let Wisort(n) be the work for isort(L)

when L is a list of length n Wins(n) is O(n)

slide-47
SLIDE 47

work

  • Let Wins(n) be the work for ins(x, L)

when x, L are values and L has length n

  • Let Wisort(n) be the work for isort(L)

when L is a list of length n Wins(n) is O(n) Wisort(0) = 1 Wisort(n) = 1 + Wins(n-1) + Wisort(n-1) for n > 0

slide-48
SLIDE 48

work

  • Let Wins(n) be the work for ins(x, L)

when x, L are values and L has length n

  • Let Wisort(n) be the work for isort(L)

when L is a list of length n Wins(n) is O(n)

slide-49
SLIDE 49

work

  • Let Wins(n) be the work for ins(x, L)

when x, L are values and L has length n

  • Let Wisort(n) be the work for isort(L)

when L is a list of length n Wins(n) is O(n) Wisort(0) = 1 Wisort(n) = O(n) + Wisort(n-1) for n > 0

slide-50
SLIDE 50

work

  • Let Wins(n) be the work for ins(x, L)

when x, L are values and L has length n

  • Let Wisort(n) be the work for isort(L)

when L is a list of length n Wins(n) is O(n) Wisort(0) = 1 Wisort(n) = O(n) + Wisort(n-1) for n > 0 Wisort(n) is O(n2)

slide-51
SLIDE 51

work

  • Let Wins(n) be the work for ins(x, L)

when x, L are values and L has length n

  • Let Wisort(n) be the work for isort(L)

when L is a list of length n Wins(n) is O(n) Wisort(0) = 1 Wisort(n) = O(n) + Wisort(n-1) for n > 0 Wisort(n) is O(n2)

THIS IS SLOW! WE CAN DO BETTER!

slide-52
SLIDE 52

mergesort

Conceptually, a merge sort works as follows:

  • 1. Divide the unsorted list into n sublists,

each containing 1 element.

  • 2. Repeatedly Merge sublists to produce new

sublists until there is only 1 sublist left.

slide-53
SLIDE 53

mergesort

Conceptually, a merge sort works as follows:

  • 1. Divide the unsorted list into n sublists,

each containing 1 element.

  • 2. Repeatedly Merge sublists to produce new

sublists until there is only 1 sublist left.

Wrong! Wrong! Wrong!

slide-54
SLIDE 54

mergesort

Conceptually, a merge sort works as follows:

  • 1. Divide the unsorted list into n sublists,

each containing 1 element.

  • 2. Repeatedly Merge sublists to produce new

sublists until there is only 1 sublist left.

Wrong! Wrong! Wrong! Doesn’t say “recursive”...

slide-55
SLIDE 55

mergesort

Conceptually, a merge sort works as follows:

  • 1. Divide the unsorted list into n sublists,

each containing 1 element.

  • 2. Repeatedly Merge sublists to produce new

sublists until there is only 1 sublist left.

Wrong! Wrong! Wrong! Doesn’t say “recursive”...

… what’s n?

slide-56
SLIDE 56

mergesort

Conceptually, a merge sort works as follows:

  • 1. Divide the unsorted list into n sublists,

each containing 1 element.

  • 2. Repeatedly Merge sublists to produce new

sublists until there is only 1 sublist left.

Wrong! Wrong! Wrong! Doesn’t say “recursive”...

… what’s n? … repeatedly????

slide-57
SLIDE 57

mergesort

Conceptually, a merge sort works as follows:

  • 1. Divide the unsorted list into n sublists,

each containing 1 element.

  • 2. Repeatedly Merge sublists to produce new

sublists until there is only 1 sublist left.

Wrong! Wrong! Wrong! Doesn’t say “recursive”...

… what’s n? … and then? … repeatedly????

slide-58
SLIDE 58

mergesort

Conceptually, a merge sort works as follows:

  • 1. Divide the unsorted list into n sublists,

each containing 1 element.

  • 2. Repeatedly Merge sublists to produce new

sublists until there is only 1 sublist left.

Wrong! Wrong! Wrong! Doesn’t say “recursive”...

… what’s n? … and then?

What’s the output? How does it relate to the input?

… repeatedly????

slide-59
SLIDE 59

mergesort

A recursive divide-and-conquer algorithm

  • If list has length 0 or 1, do nothing.
  • Otherwise,

split the list into two shorter lists, sort these two lists, merge the (sorted) results

slide-60
SLIDE 60

implementation

  • First, let’s design helper functions

split : int list -> int list * int list merge : int list * int list -> int list

slide-61
SLIDE 61

implementation

  • First, let’s design helper functions

split : int list -> int list * int list merge : int list * int list -> int list (what specs should we use?)

slide-62
SLIDE 62

implementation

  • First, let’s design helper functions

split : int list -> int list * int list merge : int list * int list -> int list (what specs should we use?) split splits a list into two sublists merge combines two sorted lists into one

slide-63
SLIDE 63

implementation

  • First, let’s design helper functions

split : int list -> int list * int list merge : int list * int list -> int list (what specs should we use?) split splits a list into two sublists merge combines two sorted lists into one

(a bit imprecise, but we’ll fix that…)

slide-64
SLIDE 64

split

ENSURES split(L) = a pair of lists (A, B) such that length(A) and length(B) differ by at most 1, and A@B is a permutation of L. split : int list -> int list * int list

slide-65
SLIDE 65

split

ENSURES split(L) = a pair of lists (A, B) such that length(A) and length(B) differ by at most 1, and A@B is a permutation of L. split : int list -> int list * int list

length(A)≈length(B)

write as

slide-66
SLIDE 66

split

ENSURES split(L) = a pair of lists (A, B) such that length(A) and length(B) differ by at most 1, and A@B is a permutation of L. split : int list -> int list * int list

slide-67
SLIDE 67

split

ENSURES split(L) = a pair of lists (A, B) such that length(A) and length(B) differ by at most 1, and A@B is a permutation of L. split : int list -> int list * int list

fun split [ ] = ([ ], [ ])

slide-68
SLIDE 68

split

ENSURES split(L) = a pair of lists (A, B) such that length(A) and length(B) differ by at most 1, and A@B is a permutation of L. split : int list -> int list * int list

fun split [ ] = ([ ], [ ]) | split [x] = ([x], [ ])

slide-69
SLIDE 69

split

ENSURES split(L) = a pair of lists (A, B) such that length(A) and length(B) differ by at most 1, and A@B is a permutation of L. split : int list -> int list * int list

fun split [ ] = ([ ], [ ]) | split [x] = ([x], [ ]) | split (x::y::L) =

slide-70
SLIDE 70

split

ENSURES split(L) = a pair of lists (A, B) such that length(A) and length(B) differ by at most 1, and A@B is a permutation of L. split : int list -> int list * int list

fun split [ ] = ([ ], [ ]) | split [x] = ([x], [ ]) | split (x::y::L) = let val (A, B) = split L in

slide-71
SLIDE 71

split

(x::A, y::B) end

ENSURES split(L) = a pair of lists (A, B) such that length(A) and length(B) differ by at most 1, and A@B is a permutation of L. split : int list -> int list * int list

fun split [ ] = ([ ], [ ]) | split [x] = ([x], [ ]) | split (x::y::L) = let val (A, B) = split L in

slide-72
SLIDE 72

split

(x::A, y::B) end

ENSURES split(L) = a pair of lists (A, B) such that length(A) and length(B) differ by at most 1, and A@B is a permutation of L. split : int list -> int list * int list

fun split [ ] = ([ ], [ ]) | split [x] = ([x], [ ]) | split (x::y::L) = let val (A, B) = split L in

note the use of list patterns and pair patterns

slide-73
SLIDE 73

split equations

split [ ] = ([ ], [ ]) split [x] = ([x], [ ]) split (x::y::L) = (x::A, y::B) end let val (A, B) = split L in

For all values x, y : int and L : int list,

slide-74
SLIDE 74

split equations

split [ ] = ([ ], [ ]) split [x] = ([x], [ ]) split (x::y::L) =

For all values x, y : int and L : int list,

slide-75
SLIDE 75

split equations

split [ ] = ([ ], [ ]) split [x] = ([x], [ ]) split (x::y::L) =

For all values x, y : int and L : int list, (x::A, y::B), where (A, B) = split L

slide-76
SLIDE 76

split equations

split [ ] = ([ ], [ ]) split [x] = ([x], [ ]) split (x::y::L) =

For all values x, y : int and L : int list, (x::A, y::B), where (A, B) = split L Can be used to calculate split R for any value R : int list

slide-77
SLIDE 77

split equations

split [ ] = ([ ], [ ]) split [x] = ([x], [ ]) split (x::y::L) =

For all values x, y : int and L : int list, (x::A, y::B), where (A, B) = split L Can be used to calculate split R for any value R : int list split [4,2,1,3] = ([4,1], [2,3])

slide-78
SLIDE 78

split equations

split [ ] = ([ ], [ ]) split [x] = ([x], [ ]) split (x::y::L) =

For all values x, y : int and L : int list, (x::A, y::B), where (A, B) = split L Can be used to calculate split R for any value R : int list split [4,2,1,3] = ([4,1], [2,3]) split [4,2,1] = ([4,1], [2])

slide-79
SLIDE 79
  • Proof: by (strong) induction on length of L
  • Base cases: L = [ ], [x]

EASY

  • Inductive case: L=x::(y::R) R is shorter than L

Assume Induction Hypothesis: split(R) = a pair (A’, B’) such

that length(A’)≈length(B’) and A’@B’ is a perm of R. Show that split(x::y::R) = a pair (A, B) such that length(A)≈length(B) and A@B is a perm of x::(y::R).

For all L:int list, split(L) = a pair of lists (A, B) such that length(A) ≈ length(B) and A@B is a permutation of L.

slide-80
SLIDE 80
  • Proof: by (strong) induction on length of L
  • Base cases: L = [ ], [x]

EASY

  • Inductive case: L=x::(y::R) R is shorter than L

Assume Induction Hypothesis: split(R) = a pair (A’, B’) such

that length(A’)≈length(B’) and A’@B’ is a perm of R. Show that split(x::y::R) = a pair (A, B) such that length(A)≈length(B) and A@B is a perm of x::(y::R).

For all L:int list, split(L) = a pair of lists (A, B) such that length(A) ≈ length(B) and A@B is a permutation of L.

split [ ] = ([ ], [ ]) split [x] = ([x], [ ])

slide-81
SLIDE 81
  • Proof: by (strong) induction on length of L
  • Base cases: L = [ ], [x]

EASY

  • Inductive case: L=x::(y::R) R is shorter than L

Assume Induction Hypothesis: split(R) = a pair (A’, B’) such

that length(A’)≈length(B’) and A’@B’ is a perm of R. Show that split(x::y::R) = a pair (A, B) such that length(A)≈length(B) and A@B is a perm of x::(y::R).

For all L:int list, split(L) = a pair of lists (A, B) such that length(A) ≈ length(B) and A@B is a permutation of L.

split [ ] = ([ ], [ ]) split [x] = ([x], [ ]) split (x::y::R) = (x::A’, y::B’) length(x::A’) ≈ length(y::B’) (x::A’)@(y::B’) is a perm of x::(y::R)

slide-82
SLIDE 82

comments

  • We used strong induction on length of L

Reason: split(x::y::R) calls split(R) and length of R is two less than length of x::y::R.

  • If length L = n > 1 and split(L) = (A, B),

A and B are shorter than L

If n is even > 1, length A = length B = n div 2 < n. If n is odd > 1, length A = (n div 2) + 1 < n, length B = n div 2 < n.

slide-83
SLIDE 83

merge

REQUIRES A and B are sorted lists ENSURES merge(A, B) = a sorted perm of A@B merge : int list * int list -> int list

slide-84
SLIDE 84

merge

REQUIRES A and B are sorted lists ENSURES merge(A, B) = a sorted perm of A@B merge : int list * int list -> int list

fun merge (A, [ ]) = A | merge ([ ], B) = B

slide-85
SLIDE 85

merge

REQUIRES A and B are sorted lists ENSURES merge(A, B) = a sorted perm of A@B merge : int list * int list -> int list

fun merge (A, [ ]) = A | merge ([ ], B) = B | merge (x::L, y::R) = case compare(x, y) of

slide-86
SLIDE 86

merge

REQUIRES A and B are sorted lists ENSURES merge(A, B) = a sorted perm of A@B merge : int list * int list -> int list

fun merge (A, [ ]) = A | merge ([ ], B) = B | merge (x::L, y::R) = case compare(x, y) of LESS => x :: merge(L, y::R)

slide-87
SLIDE 87

merge

REQUIRES A and B are sorted lists ENSURES merge(A, B) = a sorted perm of A@B merge : int list * int list -> int list

fun merge (A, [ ]) = A | merge ([ ], B) = B | merge (x::L, y::R) = case compare(x, y) of LESS => x :: merge(L, y::R) | EQUAL => x :: y :: merge(L, R)

slide-88
SLIDE 88

merge

| GREATER => y :: merge(x::L, R)

REQUIRES A and B are sorted lists ENSURES merge(A, B) = a sorted perm of A@B merge : int list * int list -> int list

fun merge (A, [ ]) = A | merge ([ ], B) = B | merge (x::L, y::R) = case compare(x, y) of LESS => x :: merge(L, y::R) | EQUAL => x :: y :: merge(L, R)

slide-89
SLIDE 89

merge

| GREATER => y :: merge(x::L, R)

REQUIRES A and B are sorted lists ENSURES merge(A, B) = a sorted perm of A@B merge : int list * int list -> int list

fun merge (A, [ ]) = A | merge ([ ], B) = B | merge (x::L, y::R) = case compare(x, y) of LESS => x :: merge(L, y::R) | EQUAL => x :: y :: merge(L, R)

We need a 3-way branch, so cased comparison is better than nested if-then-else

slide-90
SLIDE 90

merge equations

merge (A, [ ]) = A merge ([ ], B) = B | GREATER => y :: merge(x::A, B) merge (x::A, y::B) = case compare(x, y) of LESS => x :: merge(A, y::B) | EQUAL => x :: y :: merge(A, B)

For all values x, y : int and A, B : int list,

slide-91
SLIDE 91

merge equations

merge (A, [ ]) = A merge ([ ], B) = B

For all values x, y : int and A, B : int list,

slide-92
SLIDE 92

merge equations

merge (A, [ ]) = A merge ([ ], B) = B

For all values x, y : int and A, B : int list,

= y :: merge(x::A, B) merge (x::A, y::B) = x :: merge(A, y::B) = x :: y :: merge(A, B)

if x<y if x=y if x>y

slide-93
SLIDE 93

merge equations

merge (A, [ ]) = A merge ([ ], B) = B

For all values x, y : int and A, B : int list,

= y :: merge(x::A, B) merge (x::A, y::B) = x :: merge(A, y::B) = x :: y :: merge(A, B)

if x<y if x=y if x>y Can be used to evaluate merge(L, R) for all values L, R : int list

slide-94
SLIDE 94

merge equations

merge (A, [ ]) = A merge ([ ], B) = B

For all values x, y : int and A, B : int list,

= y :: merge(x::A, B) merge (x::A, y::B) = x :: merge(A, y::B) = x :: y :: merge(A, B)

if x<y if x=y if x>y merge([1,4], [2,3]) = [1,2,3,4] Can be used to evaluate merge(L, R) for all values L, R : int list

slide-95
SLIDE 95

correctness?

How do we prove this function satisfies the spec?

  • Induction, but on on what?
  • in base cases, at least one list is empty
  • in recursive calls, one or both is shorter

| GREATER => y :: merge(x::L, R) fun merge (A, [ ]) = A | merge ([ ], B) = B | merge (x::L, y::R) = case compare(x, y) of LESS => x :: merge(L, y::R) | EQUAL => x :: y :: merge(L, R)

slide-96
SLIDE 96

correctness?

How do we prove this function satisfies the spec?

  • Induction, but on on what?
  • in base cases, at least one list is empty
  • in recursive calls, one or both is shorter

| GREATER => y :: merge(x::L, R) fun merge (A, [ ]) = A | merge ([ ], B) = B | merge (x::L, y::R) = case compare(x, y) of LESS => x :: merge(L, y::R) | EQUAL => x :: y :: merge(L, R)

The product of list lengths!

slide-97
SLIDE 97

Proof: strong induction on product of lengths of A, B.

  • Base cases: (A, [ ]) and ([ ], B).

(i) Show: if A is sorted, merge(A, [ ]) = a sorted perm of A@[ ]. (ii) Show: if B is sorted, merge([ ], B) = a sorted perm of [ ]@B.

  • Inductive case: (x::A, y::B)

Assume IH: for all pairs of sorted lists (A’, B’) with smaller product

  • f lengths than (x::A, y::B), merge(A’, B’) = a sorted perm of A’@B’.

Show: if x::A and y::B are sorted, then merge(x::A, y::B) = a sorted perm of (x::A)@(y::B).

For all sorted lists A and B, merge(A, B) = a sorted permutation of A@B.

Exercise: fill in the details!

correctness

slide-98
SLIDE 98

msort

  • We proved that split and merge are correct
  • Now let’s use them to define msort

REQUIRES A and B are sorted lists ENSURES merge(A, B) = a sorted perm of A@B merge : int list * int list -> int list ENSURES split L = a pair of lists (A, B) such that length(A) ≈ length(B) and A@B is a perm of L split : int list -> int list * int list ENSURES msort L = a sorted perm of L msort : int list -> int list

slide-99
SLIDE 99

msort

ENSURES msort(L) = a sorted perm of L

msort : int list -> int list

slide-100
SLIDE 100

msort

ENSURES msort(L) = a sorted perm of L

msort : int list -> int list

fun msort [ ] = [ ]

slide-101
SLIDE 101

msort

ENSURES msort(L) = a sorted perm of L

msort : int list -> int list

fun msort [ ] = [ ] | msort [x] = [x]

slide-102
SLIDE 102

msort

ENSURES msort(L) = a sorted perm of L

msort : int list -> int list

fun msort [ ] = [ ] | msort [x] = [x] | msort L =

slide-103
SLIDE 103

msort

ENSURES msort(L) = a sorted perm of L

msort : int list -> int list

fun msort [ ] = [ ] | msort [x] = [x] | msort L = let val (A, B) = split L in

slide-104
SLIDE 104

msort

ENSURES msort(L) = a sorted perm of L

msort : int list -> int list

fun msort [ ] = [ ] | msort [x] = [x] | msort L = let val (A, B) = split L in merge (msort A, msort B)

slide-105
SLIDE 105

msort

end ENSURES msort(L) = a sorted perm of L

msort : int list -> int list

fun msort [ ] = [ ] | msort [x] = [x] | msort L = let val (A, B) = split L in merge (msort A, msort B)

slide-106
SLIDE 106

msort

end ENSURES msort(L) = a sorted perm of L

msort : int list -> int list

fun msort [ ] = [ ] | msort [x] = [x] | msort L = let val (A, B) = split L in merge (msort A, msort B)

msort [4,2,1,3] ⟹* [1,2,3,4]

slide-107
SLIDE 107

msort

end ENSURES msort(L) = a sorted perm of L

msort : int list -> int list

fun msort [ ] = [ ] | msort [x] = [x] | msort L = let val (A, B) = split L in merge (msort A, msort B)

msort [4,2,1,3] ⟹* [1,2,3,4]

msort [4,2,1,3] = merge(msort [4,1], msort [2,3]) = merge([1,4], [2,3]) = [1,2,3,4]

slide-108
SLIDE 108

[38, 27, 43, 3, 9, 82, 10] [38, 43, 9, 10] [27, 3, 82] [38, 9] [43, 10] [27, 82] [3] [38] [9] [43] [10] [27] [82] [9, 38] [10, 43] [27, 82] [9, 10, 38, 43] [3, 27, 82] [3, 9, 10, 27, 38, 43, 82] split merge

slide-109
SLIDE 109

msort equations

msort [ ] = [ ] For all values x : int and L : int list, msort [x] = [x] msort L = merge(msort A, msort B) where (A, B) = split L, if length L ≥ 2 (where did this side condition come from?)

slide-110
SLIDE 110

correctness

Proof: by strong induction on length of L

  • Base cases:

(i) Show msort [ ] = a sorted perm of [ ] (ii) Show msort [x] = a sorted perm of [x]

  • Inductive case: suppose length(L)>1.

Inductive hypothesis: for all shorter lists R, msort R = a sorted perm of R. Show that msort L = a sorted perm of L.

For all L:int list, msort(L) = a sorted perm of L.

A crucial assumption needed in proof details: length L > 1

slide-111
SLIDE 111

comments

  • The msort proof was

“by (strong) induction on the length of L”

  • msort L calls msort A and msort B,

where A and B have shorter length

  • It would not have been appropriate to say

“by structural induction on L”

  • msort (x::R) doesn’t call msort R
slide-112
SLIDE 112

work

Wsplit(n) = work of split(L) when length(L)=n Wmerge(n) = work of merge(A, B) when length(A) + length(B) = n Wsplit(n) is O(n) Wmerge(n) is O(n)

slide-113
SLIDE 113

work

Wmsort(n) = work of msort(L) when length(L)=n Wmsort(n) = Wsplit(n) + 2Wmsort(n div 2) + Wmerge(n) + 1 = O(n) + 2Wmsort(n div 2) Wmsort(n) is O(n log n) Wmsort(0) = 1 Wmsort(1) = 1 for n>1

slide-114
SLIDE 114

W(n) = n + 2 W(n div 2) = n + 2 (n div 2 + 2 W(n div 4)) = n + 2(n/2) + 4(n/4) +… + 2k (n/2k) where k = log2 n = n + n + n +… + n (k terms) Deriving the work for msort = n log2 n Simplify recurrence to: So Wmsort(n) is O(n log n) = n + 2(n/2) + 4 W(n/4) = n + 2(n/2) + 4(n/4) + 8W(n/8) This W has same asymptotic behavior as Wmsort

slide-115
SLIDE 115

summary

  • msort is correct

msort L = isort L = sorted perm of L

  • msort is (more) efficient (than isort)

Wmsort(n) is O(n log n) Wisort(n) is O(n2)

slide-116
SLIDE 116
slide-117
SLIDE 117

variations on a theme

  • Let’s consider some alternative ways

to write this function

  • Some will be correct, some not

end fun msort [ ] = [ ] | msort [x] = [x] | msort L = let val (A, B) = split L in merge (msort A, msort B)

by msort

slide-118
SLIDE 118

msort

end fun msort [ ] = [ ] | msort [x] = [x] | msort L = let val (A, B) = split L val A’ = msort A val B’ = msort B inmerge (A’, B’)

an alternative version ✓ correct ✓ work

slide-119
SLIDE 119

msort

end fun msort L = if length L < 2 then L else let val (A, B) = split L in merge (A, B)

another alternative version ✓ correct ✓ work

slide-120
SLIDE 120

msort

fun msort [ ] = [ ] | msort [x] = [x] | msort L = let val (A, B) = split L in merge (msort A, msort B) end

is this clause redundant?

slide-121
SLIDE 121

msort

fun msort [ ] = [ ] | msort L = let val (A, B) = split L in merge (msort A, msort B) end

is this clause redundant?

slide-122
SLIDE 122

after deletion

fun msort [ ] = [ ] | msort L = let val (A, B) = split L in merge (msort A, msort B) end

slide-123
SLIDE 123

after deletion

fun msort [ ] = [ ] | msort L = let val (A, B) = split L in merge (msort A, msort B) end

slide-124
SLIDE 124

after deletion

loops forever

  • n non-empty lists

fun msort [ ] = [ ] | msort L = let val (A, B) = split L in merge (msort A, msort B) end

slide-125
SLIDE 125

the problem

  • split [x] = ([x], [ ])
  • msort [x] ⟹* (fn ... => ...) (msort [x], msort [ ])

leads to infinite computation

slide-126
SLIDE 126

the problem

  • split [x] = ([x], [ ])
  • msort [x] ⟹* (fn ... => ...) (msort [x], msort [ ])

leads to infinite computation

Q: What happens if you try to prove msort correct?

slide-127
SLIDE 127

the problem

  • split [x] = ([x], [ ])
  • msort [x] ⟹* (fn ... => ...) (msort [x], msort [ ])

leads to infinite computation

Q: What happens if you try to prove msort correct? A: The proof breaks down!

slide-128
SLIDE 128

the problem

  • split [x] = ([x], [ ])
  • msort [x] ⟹* (fn ... => ...) (msort [x], msort [ ])

leads to infinite computation

Q: What happens if you try to prove msort correct? A: The proof breaks down!

Cannot assume length L > 1 in inductive step

slide-129
SLIDE 129
slide-130
SLIDE 130
  • The proof for msort relied only on the

specifications of split and merge

  • Can replace split by any other function

with the same specification, and the same proof would work!

slide-131
SLIDE 131
  • The proof for msort relied only on the

specifications of split and merge

  • Can replace split by any other function

with the same specification, and the same proof would work!

the new version

  • f msort

also sorts lists!

slide-132
SLIDE 132

example

fun split’ [ ] = ([ ], [ ]) | split’ [x] = ([ ], [x]) | split’ (x::y::L) = let val (A, B) = split’ L in (x::A, y::B) end

fun msort’ [ ] = [ ] | msort’ [x] = [x] | msort’ L = let val (A, B) = split’ L in merge(msort’ A, msort’ B) end

slide-133
SLIDE 133

example

  • split and split’ are not extensionally equivalent
  • But they both satisfy the specification

used in the correctness proof for msort and msort’

  • ... so msort and msort’ are both correct
slide-134
SLIDE 134

clause order

fun merge (A, [ ]) = A | merge ([ ], B) = B | merge (x::A, y::B) = … fun merge (x::A, y::B) = … | merge (A, [ ]) = A | merge ([ ], B) = B

  • r

ML tries patterns in the order written

slide-135
SLIDE 135

clause order

fun merge (A, [ ]) = A | merge ([ ], B) = B | merge (x::A, y::B) = …

Does clause order matter here?

fun merge (x::A, y::B) = … | merge (A, [ ]) = A | merge ([ ], B) = B

  • r

ML tries patterns in the order written

slide-136
SLIDE 136

clause order

fun merge (A, [ ]) = A | merge ([ ], B) = B | merge (x::A, y::B) = …

Does clause order matter here? NO

fun merge (x::A, y::B) = … | merge (A, [ ]) = A | merge ([ ], B) = B

  • r

ML tries patterns in the order written

slide-137
SLIDE 137

clause order

fun merge (A, [ ]) = A | merge ([ ], B) = B | merge (x::A, y::B) = …

Does clause order matter here? NO Patterns are exhaustive Overlap of first two clauses is harmless

Each yields merge([ ], [ ]) = [ ]

fun merge (x::A, y::B) = … | merge (A, [ ]) = A | merge ([ ], B) = B

  • r

ML tries patterns in the order written

slide-138
SLIDE 138

scope

  • The helper functions really helped
  • They were also useful for testing
  • But we only really cared about isort, msort
  • fun split …
  • fun merge …
  • fun msort … ;

Standard ML of New Jersey

  • fun ins …
  • fun isort … ;

Standard ML of New Jersey

val ins = fn - : int * int list -> int list val isort = fn - : int list -> int list val split = fn - : int list -> int list * int list val merge = fn - : int list * int list -> int list val msort = fn - : int list -> int list

slide-139
SLIDE 139

scope

  • There may be no good reason to make the

helper functions visible to the entire world

  • We can easily make them “private”
  • local

fun ins … in fun isort … end;

  • local

fun split … fun merge … in fun msort … end;

Standard ML of New Jersey Standard ML of New Jersey

val msort = fn - : int list -> int list val isort = fn - : int list -> int list

slide-140
SLIDE 140

conclusion

  • We implemented two well known sorting

algorithms for integer lists

  • insertion sort
  • mergesort
  • There are many others…
  • quicksort
  • bubble sort
  • selection sort
slide-141
SLIDE 141

conclusion

  • We implemented two well known sorting

algorithms for integer lists

  • insertion sort
  • mergesort
  • There are many others…
  • quicksort
  • bubble sort
  • selection sort
slide-142
SLIDE 142

coming soon

  • Generalizing from int to an ordered type
  • Generalizing from lists to trees of data

Advantages of functional programming