33: Accumulators & Polishing code (Functional Data) - - PowerPoint PPT Presentation

33 accumulators polishing code functional data
SMART_READER_LITE
LIVE PREVIEW

33: Accumulators & Polishing code (Functional Data) - - PowerPoint PPT Presentation

33: Accumulators & Polishing code (Functional Data) Accumulators Estimated Value and search subtrees Functional data structures Strategies we've seen so far "Strengthen the recursion": solve a more general problem


slide-1
SLIDE 1

33: Accumulators & Polishing code (Functional Data)

Accumulators Estimated Value and search subtrees Functional data structures

slide-2
SLIDE 2

Strategies we've seen so far

  • "Strengthen the recursion": solve a more general problem…
  • Often "add another argument
  • sometimes an extra argument is just a counter (today's quiz) or a yes/no toggle
  • sometimes more
  • "fancy censor starting with either * or +" vs "fancy censor"
  • bignum-add-with-carry rather than bignum-add
  • …to make your recursive result more useful to you as you construct the
  • verall result
  • T
  • day's quiz!
  • Accumulators or other stored data to help with recursion
  • Reverse! (which also uses idea 1)
  • Divide and conquer (mergesort)
  • Break problem into manageable pieces (Rackette)
slide-3
SLIDE 3

Reverse a list, slow version

  • OI. [3, 4, 1, 5, 6]
  • RI. [4, 1, 5, 6]

RR [6, 5, 1, 4] append a list containing the head of OI to the recursive result OR [6, 5, 1, 4, 3]

slide-4
SLIDE 4

Reverse a list, slow version

/* reverse: list('a) => list('a) ** Input: a list of items ** Output: a list containing the same items, ** in reverse order */ let rec slowReverse: list('a) => list('a) = fun | [] => [] | [hd, ...tl] => slowReverse(tl) @ [hd]; Because append (@) takes time proportional to the length of the fjrst list, the runtime is in O(n => n^2).

slide-5
SLIDE 5

Improve reverse!

/* reverseHelp: (list('a), list('a)) => list('a) ** Input: ** start, a list of items ** partial, a list to append to the reversed version of "start" ** Output: a list containing the items in "start" in reverse order, ** followed by those in "partial" */ let rec reverseHelp = ... let fastReverse: list('a) => list('a) = input => reverse_help(input, []);

Run time of fastReverse is that of reverseHelp

slide-6
SLIDE 6

reverseHelp

/* reverseHelp: (list('a), list('a)) => list('a) ** Input: ** start, a list of items ** partial, a list to append to the reversed version of "start" ** Output: a list containing the items in "start" in reverse order, ** followed by those in "partial" */ let rec reverseHelp = (start, partial) => switch(start) { | [] => partial | [hd, ... tl] => reverseHelp(tl, [hd, ...partial]) }; Runtime: linear in length of "start", so that fastReverse is linear-time.

slide-7
SLIDE 7

What makes reverseHelp work?

  • Secret sauce: save your "partial work" (the reverse of

the initial items in the list) using a second argument,

  • ne in which you "accumulate" results.
  • This extra argument is called an "accumulator"
  • This is also what goes on "fold", for instance.
  • Idea is used repeatedly in this week's HW.
slide-8
SLIDE 8

Quiz

  • let mapi: ((int, 'a) => 'b, list('a)) => list('b); Same as

List.map, but the function is applied to the index of the element as fjrst argument (counting from 0), and the element itself as second argument. mapi( (x, y) => (x, y), ["a", "b", "c"]) produces [(0, "a"), (1, "b"), (2, "c")] mapi( (x, y) => x*y, [3, 1, 4]) produces [0, 1, 8]

slide-9
SLIDE 9

Recursion Diagram

OI "+",[1, 3, 4] RI"+",[3, 4] RO [3, 5] uh…add one to each item in RO, then cons on 1 + 0? That won't work when it's "*" instead of "+" I really needed to add 1 to 3 and 2 to 4 OO [1, 4, 6]

slide-10
SLIDE 10

Strengthen the recursion!

let mapi = (f, alod) => mapiHelper(f, alod, 0);

  • This helper simply works like mapi, but "counting up" from the

third argument, rather than from 0.

  • Type signature for mapiHelper
  • Code for mapiHelper

let rec mapiHelper = (f, alod, n) => switch(alod) { | [] => ??? /* JUST THESE | [hd, ... tl] => [f(n, hd), ... ???] ** TWO LINES */ };

slide-11
SLIDE 11

Complicated programs are diffjcult to debug and maintain

slide-12
SLIDE 12
  • It's always worth splitting into subtasks
  • It's always worth cleaning up and simplifying code
  • "Polished" code is nice to look at
  • Makes us more inclined to jump in and debug it
slide-13
SLIDE 13

An Example (courtesy of a fellow student; code slightly edited)

slide-14
SLIDE 14

let rec iroot: (int, int, int => int) => int = (n, m, proc) =>{ let newBound = (n+m)/2; let procApply = proc(newBound); let procApplyN = proc(n); switch(procApply){ |x when x > 0 => { if(procApplyN > 0){ if(newBound-n == 1){ n } else iroot(newBound, m, proc) } else if (procApplyN < 0) { if(newBound-n == 1){ n } else iroot(n, newBound, proc) } else { newBound } }

|x when x < 0 => { if(procApplyN > 0){ if(newBound-n == 1){ n } else iroot(n, newBound, proc) } else if (procApplyN < 0) { if(newBound-n == 1){ n } else iroot(newBound, m, proc) } else { newBound } } |0 => newBound | _ => failwith("Incomplete Match Case error") }; }

40 lines!

slide-15
SLIDE 15

let rec iroot: (int, int, int => int) => int = (n, m, proc) =>{ let newBound = (n+m)/2; let procApply = proc(newBound); let procApplyN = proc(n); switch(procApply){ |x when x > 0 => { if(procApplyN > 0){ if(newBound-n == 1){ n } else iroot(newBound, m, proc) } else if (procApplyN < 0) { if(newBound-n == 1){ n } else iroot(n, newBound, proc) } else { newBound } } |x when x < 0 => { if(procApplyN > 0){ if(newBound-n == 1){ n } else iroot(n, newBound, proc) } else if (procApplyN < 0) { if(newBound-n == 1){ n } else iroot(newBound, m, proc) } else { newBound } } |0 => newBound | _ => failwith("Incomplete Match Case error") }; }

slide-16
SLIDE 16

let rec iroot: (int, int, int => int) => int = (a, b, proc) =>{ let newBound = (a+b)/2; let procApply = proc(newBound); let procApplyN = proc(a); switch(procApply){ |x when x > 0 => { if(procApplyN > 0){ if(newBound-a == 1){ a } else iroot(newBound, b, proc) } else if (procApplyN < 0) { if(newBound-a == 1){ a } else iroot(a, newBound, proc) } else { newBound } } |x when x < 0 => { if(procApplyN > 0){ if(newBound-a == 1){ a } else iroot(a, newBound, proc) } else if (procApplyN < 0) { if(newBound-a == 1){ a } else iroot(newBound, b, proc) } else { newBound } } |0 => newBound | _ => failwith("Incomplete Match Case error") }; }

slide-17
SLIDE 17

let rec iroot: (int, int, int => int) => int = (a, b, proc) =>{ let newBound = (a+b)/2; let procApply = proc(newBound); let procApplyN = proc(a); switch(procApply){ |x when x > 0 => { if(procApplyN > 0){ if(newBound-a == 1){ a } else iroot(newBound, b, proc) } else if (procApplyN < 0) { if(newBound-a == 1){ a } else iroot(a, newBound, proc) } else { newBound } } |x when x < 0 => { if(procApplyN > 0){ if(newBound-a == 1){ a } else iroot(a, newBound, proc) } else if (procApplyN < 0) { if(newBound-a == 1){ a } else iroot(newBound, b, proc) } else { newBound } } |0 => newBound | _ => failwith("Incomplete Match Case error") }; }

slide-18
SLIDE 18

let rec iroot: (int, int, int => int) => int = (a, b, proc) =>{ let newBound = (a + b) / 2; let procApply = proc(newBound); let procApplyN = proc(a); switch (procApply){ | x when x > 0 => { if (procApplyN > 0){ if (newBound - a == 1){ a } else iroot(newBound, b, proc) } else if (procApplyN < 0) { if (newBound - a == 1){ a } else iroot(a, newBound, proc) } else { newBound } } | x when x < 0 => { if (procApplyN > 0){ if (newBound - a == 1){ a } else iroot(a, newBound, proc) } else if (procApplyN < 0) { if (newBound - a == 1){ a } else iroot(newBound, b, proc) } else { newBound } } | 0 => newBound | _ => failwith("Incomplete Match Case error") }; }

slide-19
SLIDE 19

let rec iroot: (int, int, int => int) => int = (a, b, proc) =>{ let newBound = (a + b) / 2; let procApply = proc(newBound); let procApplyN = proc(a); switch (procApply){ | x when x > 0 => { if (procApplyN > 0){ if (newBound - a == 1){ a } else iroot(newBound, b, proc) } else if (procApplyN < 0) { if (newBound - a == 1){ a } else iroot(a, newBound, proc) } else { newBound } } | x when x < 0 => { if (procApplyN > 0){ if (newBound - a == 1){ a } else iroot(a, newBound, proc) } else if (procApplyN < 0) { if (newBound - a == 1){ a } else iroot(newBound, b, proc) } else { newBound } } | 0 => newBound | _ => failwith("Incomplete Match Case error") }; }

slide-20
SLIDE 20

let rec iroot: (int, int, int => int) => int = (a, b, proc) =>{ let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); switch (midVal){ | x when x > 0 => { if (leftVal > 0){ if (mid - a == 1){ a } else iroot(mid, b, proc) } else if (leftVal < 0) { if (mid - a == 1){ a } else iroot(a, mid, proc) } else { mid } } | x when x < 0 => { if (leftVal > 0){ if (mid - a == 1){ a } else iroot(a, mid, proc) } else if (leftVal < 0) { if (mid - a == 1){ a } else iroot(mid, b, proc) } else { mid } } | 0 => mid | _ => failwith("Incomplete Match Case error") }; }

slide-21
SLIDE 21

let rec iroot: (int, int, int => int) => int = (a, b, proc) =>{ let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); switch (midVal){ | x when x > 0 => { if (leftVal > 0){ if (mid - a == 1){ a } else iroot(mid, b, proc) } else if (leftVal < 0) { if (mid - a == 1){ a } else iroot(a, mid, proc) } else { mid } } | x when x < 0 => { if (leftVal > 0){ if (mid - a == 1){ a } else iroot(a, mid, proc) } else if (leftVal < 0) { if (mid - a == 1){ a } else iroot(mid, b, proc) } else { mid } } | 0 => mid | _ => failwith("Incomplete Match Case error") }; }

slide-22
SLIDE 22

let rec iroot: (int, int, int => int) => int = (a, b, proc) =>{ let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); let length = mid – a; switch (midVal){ | x when x > 0 => { if (leftVal > 0){ if (length == 1){ a } else iroot(mid, b, proc) } else if (leftVal < 0) { if (length == 1){ a } else iroot(a, mid, proc) } else { mid } }

| x when x < 0 => { if (leftVal > 0){ if (length == 1){ a } else iroot(a, mid, proc) } else if (leftVal < 0) { if (length == 1){ a } else iroot(mid, b, proc) } else { mid } } | 0 => mid | _ => failwith("Incomplete Match Case error") }; }

slide-23
SLIDE 23

let rec iroot: (int, int, int => int) => int = (a, b, proc) =>{ let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); let short = (mid – a == 1); switch (midVal){ | x when x > 0 => { if (leftVal > 0){ if (short) { a } else iroot(mid, b, proc) } else if (leftVal < 0) { if (short) { a } else iroot(a, mid, proc) } else { mid } }

| x when x < 0 => { if (leftVal > 0){ if (short) { a } else iroot(a, mid, proc) } else if (leftVal < 0) { if (short) { a } else iroot(mid, b, proc) } else { mid } } | 0 => mid | _ => failwith("Incomplete Match Case error") }; }

slide-24
SLIDE 24

let rec iroot: (int, int, int => int) => int = (a, b, proc) =>{ let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); let short = (mid – a == 1); switch (midVal){ | x when x > 0 => { if (leftVal > 0){ if (short) { a } else iroot(mid, b, proc) } else if (leftVal < 0) { if (short) { a } else iroot(a, mid, proc) } else { mid } }

| x when x < 0 => { if (leftVal > 0){ if (short) { a } else iroot(a, mid, proc) } else if (leftVal < 0) { if (short) { a } else iroot(mid, b, proc) } else { mid } } | 0 => mid | _ => failwith("Incomplete Match Case error") }; }

slide-25
SLIDE 25

let rec iroot: (int, int, int => int) => int = (a, b, proc) =>{ let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); let short = (mid – a == 1); if (short) a else switch (midVal){ | x when x > 0 => { if (leftVal > 0){ iroot(mid, b, proc) } else if (leftVal < 0) { iroot(a, mid, proc) } else { mid } } | x when x < 0 => { if (leftVal > 0){ iroot(a, mid, proc) } else if (leftVal < 0) { iroot(mid, b, proc) } else { mid } } | 0 => mid | _ => failwith("Incomplete Match Case error") }; }

Now apparent there's an error: if mid = a + 1, we return a, whether the zero is between a and mid, or between mid and b! We'll leave this for now…

slide-26
SLIDE 26

let rec iroot: (int, int, int => int) => int = (a, b, proc) =>{ let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); let short = (mid – a == 1); if (short) a else switch (midVal){ | x when x > 0 => { if (leftVal > 0){ iroot(mid, b, proc) } else if (leftVal < 0) { iroot(a, mid, proc) } else { mid } } | x when x < 0 => { if (leftVal > 0){ iroot(a, mid, proc) } else if (leftVal < 0) { iroot(mid, b, proc) } else { mid } } | 0 => mid | _ => failwith("Incomplete Match Case error") }; }

Let's clean up a little

slide-27
SLIDE 27

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); let short = mid - a == 1; if (short) { a; } else { switch (midVal) { | x when x > 0 => if (leftVal > 0) { iroot(mid, b, proc); } else if (leftVal < 0) { iroot(a, mid, proc); } else { mid; }

| x when x < 0 => if (leftVal > 0) { iroot(a, mid, proc); } else if (leftVal < 0) { iroot(mid, b, proc); } else { mid; } | 0 => mid | _ => failwith("Incomplete Match Case error") }; }; };

We seem to be working with midVal and leftVal a lot. If either is zero…we know an answer!

slide-28
SLIDE 28

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); let short = mid - a == 1; if (short) { a; } else { if (midVal == 0) {mid} else if (leftVal == 0) {a} else switch (midVal) { | x when x > 0 => if (leftVal > 0) { iroot(mid, b, proc); } else if (leftVal < 0) { iroot(a, mid, proc); } else { mid; }

| x when x < 0 => if (leftVal > 0) { iroot(a, mid, proc); } else if (leftVal < 0) { iroot(mid, b, proc); } else { mid; } | 0 => mid | _ => failwith("Incomplete Match Case error") }; }; };

Now we know they're both nonzero… but we keep testing midVal > 0 or leftVal > 0

slide-29
SLIDE 29

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); let short = mid - a == 1; if (short) { a; } else { if (midVal == 0) {mid} else if (leftVal == 0) {a} else switch (midVal) { | x when x > 0 => if (leftVal > 0) { iroot(mid, b, proc); } else if (leftVal < 0) { iroot(a, mid, proc); } else { mid; }

| x when x < 0 => if (leftVal > 0) { iroot(a, mid, proc); } else if (leftVal < 0) { iroot(mid, b, proc); } else { mid; } | 0 => mid | _ => failwith("Incomplete Match Case error") }; }; };

That last case can go away…and if we switch on "midVal > 0", things simplify!

slide-30
SLIDE 30

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); let short = mid - a == 1; if (short) { a; } else { if (midVal == 0) {mid} else if (leftVal == 0) {a} else switch (midVal > 0) { | true => if (leftVal > 0) { iroot(mid, b, proc); } else if (leftVal < 0) { iroot(a, mid, proc); } else { mid; }

| false => if (leftVal > 0) { iroot(a, mid, proc); } else if (leftVal < 0) { iroot(mid, b, proc); } else { mid; } }; }; };

That last case can go away…and if we switch on "midVal > 0", things simplify!

slide-31
SLIDE 31

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); let short = mid - a == 1; if (short) { a; } else { if (midVal == 0) {mid} else if (leftVal == 0) {a} else switch (midVal > 0) { | true => if (leftVal > 0) { iroot(mid, b, proc); } else if (leftVal < 0) { iroot(a, mid, proc); } else { mid; } | false => if (leftVal > 0) { iroot(a, mid, proc); } else if (leftVal < 0) { iroot(mid, b, proc); } else { mid; } }; }; };

The fjnal case (in purple) for each clause can go away…

slide-32
SLIDE 32

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); let short = mid - a == 1; if (short) { a; } else { if (midVal == 0) {mid} else if (leftVal == 0) {a} else switch (midVal > 0) { | true => if (leftVal > 0) { iroot(mid, b, proc); } else if (leftVal < 0) { iroot(a, mid, proc); }

| false => if (leftVal > 0) { iroot(a, mid, proc); } else if (leftVal < 0) { iroot(mid, b, proc); } }; }; };

The else-ifs become "else" because if it's not positive, it's negative….

slide-33
SLIDE 33

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); let short = mid - a == 1; if (short) { a; } else { if (midVal == 0) {mid} else if (leftVal == 0) {a} else switch (midVal > 0) { | true => if (leftVal > 0) { iroot(mid, b, proc); } else { iroot(a, mid, proc); }

| false => if (leftVal > 0) { iroot(a, mid, proc); } else { iroot(mid, b, proc); } }; }; };

The else-ifs become "else" because if it's not positive, it's negative….

slide-34
SLIDE 34

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); let short = mid - a == 1; if (short) { a; } else { if (midVal == 0) {mid} else if (leftVal == 0) {a} else switch (midVal > 0) { | true => if (leftVal > 0) { iroot(mid, b, proc); } else { iroot(a, mid, proc); }

| false => if (leftVal > 0) { iroot(a, mid, proc); } else { iroot(mid, b, proc); } }; }; };

Switching on a boolean is weird…normally we'd replace with "if"… but there's leftVal > 0 too!

slide-35
SLIDE 35

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); let short = mid - a == 1; if (short) { a; } else { if (midVal == 0) {mid} else if (leftVal == 0) {a} else switch (midVal > 0, leftVal > 0) { | (true, true) => iroot(mid, b, proc); | (true, false)=> iroot(a, mid, proc); | (false, true) => iroot(a, mid, proc); | (false, false) => iroot(mid, b, proc); }; }; };

Neaten up indentation again…

slide-36
SLIDE 36

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); let short = mid - a == 1; if (short) { a; } else if (midVal == 0) { mid; } else if (leftVal == 0) { a; } else { switch (midVal > 0, leftVal > 0) { | (true, true) => iroot(mid, b, proc) | (true, false) => iroot(a, mid, proc) | (false, true) => iroot(a, mid, proc) | (false, false) => iroot(mid, b, proc) }; }; };

The right-hand side of middle two cases seem the same; right-hand side of outer two cases seem the same…

slide-37
SLIDE 37

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); let short = mid - a == 1; if (short) { a; } else if (midVal == 0) { mid; } else if (leftVal == 0) { a; } else { switch (midVal > 0, leftVal > 0) { | (true, true) => iroot(mid, b, proc) | (false, false) => iroot(mid, b, proc) | (true, false) => iroot(a, mid, proc) | (false, true) => iroot(a, mid, proc) }; }; };

In fjrst pair, booleans are same; in second, they difger

slide-38
SLIDE 38

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); let short = (mid - a == 1); if (short) { a; } else if (midVal == 0) { mid; } else if (leftVal == 0) { a; } else { switch ((midVal > 0) == (leftVal > 0)) { | true => iroot(mid, b, proc) | false => iroot(a, mid, proc) }; }; };

We defjne "short", then use it just once…

slide-39
SLIDE 39

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); if (mid - a == 1) { a; } else if (midVal == 0) { mid; } else if (leftVal == 0) { a; } else { switch ((midVal > 0) == (leftVal > 0)) { | true => iroot(mid, b, proc) | false => iroot(a, mid, proc) }; }; };

We defjne "short", then use it just once…

slide-40
SLIDE 40

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); if (mid - a == 1) { a; } else if (midVal == 0) { mid; } else if (leftVal == 0) { a; } else { switch ((midVal > 0) == (leftVal > 0)) { | true => iroot(mid, b, proc) | false => iroot(a, mid, proc) }; }; };

Now we can look at making the code right! Obs 1: if b – a > 1, then mid is difgerent from both b and a Obs 2: if b – a = 1, then the interval [a, a+1] contains a zero Obs 3: no harm in recurring all the way to an interval of length one. (Log cost)

slide-41
SLIDE 41

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { if (b == a + 1) {a} else { let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); if (mid - a == 1) { a; } else if (midVal == 0) { mid; } else if (leftVal == 0) { a; } else { switch ((midVal > 0) == (leftVal > 0)) { | true => iroot(mid, b, proc) | false => iroot(a, mid, proc) }; }; }; };

Now we can look at making the code right! Obs 1: if b – a > 1, then mid is difgerent from both b and a Obs 2: if b – a = 1, then the interval [a, a+1] contains a zero Obs 3: no harm in recurring all the way to an interval of length one (Log cost)

slide-42
SLIDE 42

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { if (b == a + 1) {a} else { let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); if (mid - a == 1) { a; } else if (midVal == 0) { mid; } else if (leftVal == 0) { a; } else { switch ((midVal > 0) == (leftVal > 0)) { | true => iroot(mid, b, proc) | false => iroot(a, mid, proc) }; }; }; };

Now we can look at making the code right! Obs 1: if b – a > 1, then mid is difgerent from both b and a Obs 2: if b – a = 1, then the interval [a, a+1] contains a zero Obs 3: no harm in recurring all the way to an interval of length one (Log cost)

slide-43
SLIDE 43

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { if (b == a + 1) {a} else { let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); if (midVal == 0) { mid; } else if (leftVal == 0) { a; } else { switch ((midVal > 0) == (leftVal > 0)) { | true => iroot(mid, b, proc) | false => iroot(a, mid, proc) }; }; }; };

Now we can look at making the code right! Obs 1: if b – a > 1, then mid is difgerent from both b and a Obs 2: if b – a = 1, then the interval [a, a+1] contains a zero Obs 3: no harm in recurring all the way to an interval of length one (Log cost)

slide-44
SLIDE 44

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { if (b == a + 1) {a} else { let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); switch ((midVal > 0) == (leftVal > 0)) { | true => iroot(mid, b, proc) | false => iroot(a, mid, proc) }; }; };

Now we can look at making the code right! Obs 1: if b – a > 1, then mid is difgerent from both b and a Obs 2: if b – a = 1, then the interval [a, a+1] contains a zero Obs 3: no harm in recurring all the way to an interval of length one (Log cost)

slide-45
SLIDE 45

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { if (b == a + 1) {a} else { let mid = (a + b) / 2; let midVal = proc(mid); let leftVal = proc(a); switch ((midVal > 0) == (leftVal > 0)) { | true => iroot(mid, b, proc) | false => iroot(a, mid, proc) }; }; };

midVal and leftVal defjned, then used only once

slide-46
SLIDE 46

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { if (b == a + 1) {a} else { let mid = (a + b) / 2; switch ((proc(mid) > 0) == (proc(a) > 0)) { | true => iroot(mid, b, proc) | false => iroot(a, mid, proc) }; }; };

midVal and leftVal defjned, then used only once

slide-47
SLIDE 47

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { if (b == a + 1) {a} else { let mid = (a + b) / 2; switch ((proc(mid) > 0) == (proc(a) > 0)) { | true => iroot(mid, b, proc) | false => iroot(a, mid, proc) }; }; };

switching on T/F is weird

slide-48
SLIDE 48

let rec iroot: (int, int, int => int) => int = (a, b, proc) => { if (b == a + 1) {a} else { let mid = (a + b) / 2; if ((proc(mid) > 0) == (proc(a) > 0)) { iroot(mid, b, proc) } else { iroot(a, mid, proc)}; }; };

switching on T/F is weird Also: reorder tests to go left-to-right

slide-49
SLIDE 49

let rec iroot: (int, int, int => int) => int = (a, b, proc) => if (b == a + 1) {a} else { let mid = (a + b) / 2; if ((proc(a) > 0) == (proc(mid) > 0)) { iroot(mid, b, proc) } else { iroot(a, mid, proc) }; };

We're now using the fact that if proc has difgerent "signs" (positive, negative, OR zero) at the two ends of the interv then there's a root in the interval. Need to add this to the spec for proc!

slide-50
SLIDE 50

/* input: a, lower endpoint of an interval b, upper endpoint of an interval, b > a. proc, an int->int function whose signs (positive, zero, negative) at a and b differ

  • utput:

a number n between a and b-1, inclusive, with proc(n) and proc(n+1) having differing signs, so that the interval [n, n+1] contains a zero of proc. */ let rec iroot: (int, int, int => int) => int = (a, b, proc) => if (b == a + 1) {a} else { let mid = (a + b) / 2; if ((proc(a) > 0) == (proc(mid) > 0)) { iroot(mid, b, proc) } else { iroot(a, mid, proc) }; };

10 lines!

slide-51
SLIDE 51

let rec iroot: (int, int, int => int) => int = (n, m, proc) =>{ let newBound = (n+m)/2; let procApply = proc(newBound); let procApplyN = proc(n); switch(procApply){ |x when x > 0 => { if(procApplyN > 0){ if(newBound-n == 1){ n } else iroot(newBound, m, proc) } else if (procApplyN < 0) { if(newBound-n == 1){ n } else iroot(n, newBound, proc) } else { newBound } }

|x when x < 0 => { if(procApplyN > 0){ if(newBound-n == 1){ n } else iroot(n, newBound, proc) } else if (procApplyN < 0) { if(newBound-n == 1){ n } else iroot(newBound, m, proc) } else { newBound } } |0 => newBound | _ => failwith("Incomplete Match Case error") }; }