15-150 Fall 2020 Lecture 12 Stephen Brookes Midterm 1 Tuesday, October 13 No class that day MIT study says if you get more Z’s, you’ll get more A’s
Font of all knowledge? Australian researchers developed a typeface to help students cramming for exams. A study found a small increase in the amount remembered. The font has a back slant and gaps. “If something is too easy to read, it doesn’t create a memory trace.” Study for the Midterm!
and now… • Generalizing the subset-sum problem • Developing specs and code together • A lesson in program design We’re dealing with functions for problems that need careful specifications We may be informal (“positive integer x”) or more formal (“x > 0”) But whatever we do, MUST be clear
making change • Given a non-negative integer n, a list L of positive integers, and a constraint p : int list -> bool • Is there a sublist of L that satisfies p and adds up to n?
making change • Given a non-negative integer n, a list L of positive integers, and a constraint p : int list -> bool • Is there a sublist of L that satisfies p and adds up to n? fun sublists L = foldr ( fn (x, S) => S @ (map ( fn A => x::A) S)) [ [ ] ] L
making change • Given a non-negative integer n, a list L of positive integers, and a constraint p : int list -> bool • Is there a sublist of L that satisfies p and adds up to n? fun sublists L = foldr ( fn (x, S) => S @ (map ( fn A => x::A) S)) [ [ ] ] L val sublists = fn : 'a list -> 'a list list - sublists [1,2,3]; val it = [[],[3],[2],[2,3],[1],[1,3],[1,2],[1,2,3]] : int list list
badchange : int * int list -> (int list -> bool) -> bool A non-recursive function that returns a boolean
badchange : int * int list -> (int list -> bool) -> bool A non-recursive function that returns a boolean fun exists q = foldl ( fn (x, t) => q x orelse t) false val sum = foldl ( op + ) 0
badchange : int * int list -> (int list -> bool) -> bool A non-recursive function that returns a boolean fun exists q = foldl ( fn (x, t) => q x orelse t) false val sum = foldl ( op + ) 0 fun badchange (n, L) p = exists ( fn A => sum A = n andalso p A) (sublists L)
badchange : int * int list -> (int list -> bool) -> bool A non-recursive function that returns a boolean fun exists q = foldl ( fn (x, t) => q x orelse t) false val sum = foldl ( op + ) 0 fun badchange (n, L) p = exists ( fn A => sum A = n andalso p A) (sublists L) badchange satisfies the spec
badchange : int * int list -> (int list -> bool) -> bool A non-recursive function that returns a boolean fun exists q = foldl ( fn (x, t) => q x orelse t) false val sum = foldl ( op + ) 0 fun badchange (n, L) p = exists ( fn A => sum A = n andalso p A) (sublists L) badchange satisfies the spec but it’s inefficient!
critique badchange (300, [1,2,3,...,24]) ( fn _ => true ) ========>* true (VERY SLOW!)
critique badchange (300, [1,2,3,...,24]) ( fn _ => true ) ========>* true (VERY SLOW!) brute force search • generates the list of all sublists • and tests them, sequentially
critique badchange (300, [1,2,3,...,24]) ( fn _ => true ) ========>* true (VERY SLOW!) brute force search • generates the list of all sublists (2 24 ) • and tests them, sequentially
critique badchange (300, [1,2,3,...,24]) ( fn _ => true ) ========>* true (VERY SLOW!) brute force search • generates the list of all sublists (2 24 ) • and tests them, sequentially … only the final sublist will work!
critique badchange (300, [1,2,3,...,24]) ( fn _ => true ) ========>* true (VERY SLOW!) brute force search • generates the list of all sublists (2 24 ) • and tests them, sequentially … only the final sublist will work! 1 + 2 + … + 24 = 300
specification “can we make change for (n, L) that satisfies p?” change : int * int list -> (int list -> bool) -> bool REQUIRES p is total , n ≥ 0, L a list of positive integers ENSURES change (n, L) p = true if there is a sublist A of L with sum A = n and p A = true change (n, L) p = false , otherwise + must be faster!
a better strategy Avoid building the list of sublists • only call p on sublists with the correct sum Deal with special cases first • n = 0 • n > 0, L = [ ] For n > 0, L = x::R, use recursion ... the spec suggests this might be feasible!
change : int * int list -> (int list -> bool) -> bool A recursive function that returns a boolean p [ ] fun change (0, L) p = | change (n, [ ]) p = false | change (n, x::R) p = if x <= n then change (n-x, R) ( fn A => p(x::A)) orelse change (n, R) p else change (n, R) p
change : int * int list -> (int list -> bool) -> bool A recursive function that returns a boolean p [ ] fun change (0, L) p = | change (n, [ ]) p = false | change (n, x::R) p = if x <= n then change (n-x, R) ( fn A => p(x::A)) orelse change (n, R) p else change (n, R) p change (300, [1,2,3,...,24]) ( fn _ => true )
change : int * int list -> (int list -> bool) -> bool A recursive function that returns a boolean p [ ] fun change (0, L) p = | change (n, [ ]) p = false | change (n, x::R) p = if x <= n then change (n-x, R) ( fn A => p(x::A)) orelse change (n, R) p else change (n, R) p change (300, [1,2,3,...,24]) ( fn _ => true ) (very fast) ⟹ true
equivalently fun change (0, L) p = p [ ] | change (n, [ ]) p = false | change (n, x::R) p = if x <= n then case change (n-x, R) ( fn A => p(x::A)) of true => true | false => change (n, R) p else change (n, R) p (you’ll soon see why we mention this)
type check change : int * int list -> (int list -> bool) -> bool fun change (0, L) p = p [ ] | change (n, [ ]) p = false | change (n, x::R) p = if x <= n then change (n-x, R) ( fn A => p(x::A)) orelse change (n, R) p else change (n, R) p CHECK that each clause fits this type: the LHS patterns match types int * int list and int list -> bool, and give bindings such that, assuming change : int * int list -> (int list -> bool) -> bool, the RHS expression gets type bool
spec check fun change (0, L) p = p [ ] | change (n, [ ]) p = false | change (n, x::R) p = if x <= n then change (n-x, R) ( fn A => p(x::A)) orelse change (n, R) p else change (n, R) p REQUIRES p is total , n ≥ 0, L a list of positive integers ENSURES change (n, L) p = true iff there is a sublist A of L with sum A = n and p A = true CHECK if requires holds for (n, L, p) and then requires holds for (n ’ , L ’ , p ’ ) change (n, L) p calls change (n ’ ,L ’ ) p ’ and original call satisfies ensures and recursive call satisfies ensures
benefits • The type check shows that the function has the intended type(!) - Use type check to guide the code design - ML will give same type (or a more general one) • The spec check is basically a sketch of an inductive correctness proof - Use spec check to guide the code design - Will then be easy to give a fully detailed proof of correctness
correctness For all positive integer lists L, n ≥ 0, and total functions p : int list -> bool, change (n, L) p = true if there is a sublist A of L with sum A = n and p A = true change (n, L) p = false , otherwise • PROOF: induction on L why?
examples change (10, [5,2,5]) ( fn _ => true ) = true change (210, [1,2,3,...,20]) ( fn _ => true ) ⟹ * true (FAST!) change (10, [10,5,2,5]) ( fn A => length(A)>1) = true change (10, [10,5,2]) ( fn A => length(A)>1) = false
the right question? change : int * int list -> (int list -> bool) -> bool
the right question? change : int * int list -> (int list -> bool) -> bool I s there a sublist that adds to 300?
the right question? change : int * int list -> (int list -> bool) -> bool I s there a val it = true sublist that (YES) adds to 300?
the right question? change : int * int list -> (int list -> bool) -> bool I s there a val it = true sublist that (YES) adds to 300? What is it?
the right question? change : int * int list -> (int list -> bool) -> bool I s there a val it = true sublist that (YES) adds to 300? Y ou What is it? didn’t ask
boolean blindness • A truth value only gives a bit of information • From the context, we know true (yes) means “it’s possible to make change...” • But what if we want more information ? • “a list that works, if there is one”
boolean blindness • A truth value only gives a bit of information • From the context, we know true (yes) means “it’s possible to make change...” • But what if we want more information ? • “a list that works, if there is one” NEED a function that computes a suitable sublist, if there is one
boolean blindness • A truth value only gives a bit of information • From the context, we know true (yes) means “it’s possible to make change...” • But what if we want more information ? • “a list that works, if there is one” NEED PLAN a function that a function that computes a suitable sublist, returns an if there is one (int list) option
Recommend
More recommend