33 accumulators polishing code functional data
play

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


  1. 33: Accumulators & Polishing code (Functional Data) Accumulators Estimated Value and search subtrees Functional data structures

  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 overall result • T oday'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)

  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]

  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).

  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

  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.

  7. What makes reverseHelp work? • Secret sauce: save your "partial work" (the reverse of the initial items in the list) using a second argument, one 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.

  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]

  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]

  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 */ };

  11. Complicated programs are diffjcult to debug and maintain

  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

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

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

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

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

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

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

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

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

Recommend


More recommend