250 likes | 342 Views
CSC 313 – Advanced Programming Topics. Lecture 12: Eliminating Recursion. Why Recurse ?. Recursion is useful, powerful technique Often yields compact, easy-to-read code Highlights all possible cases making testing simple Some algorithms naturally recursive
E N D
CSC 313 – Advanced Programming Topics Lecture 12:Eliminating Recursion
Why Recurse? • Recursion is useful, powerful technique • Often yields compact, easy-to-read code • Highlights all possible cases making testing simple • Some algorithms naturally recursive • Divide-and-conquer algorithms • Nearly anything that uses a tree • LISP & other functional language programs • Recursion can reduce development time
Why Not Use Recursion? • Often means values computed repeatedly • Cannot store interim results between calls • Short, simple methods limit optimizations • Behavior varies widely between calls • Compiler cannot optimize over method calls • Requires lots of method calls • But good design emphasizes calling methods • Cannot say this is bad in other cases • Why are recursive method calls bad?
Why Not Use Recursion? • Often means values computed repeatedly • Cannot store interim results between calls • Short, simple methods limit optimizations • Behavior varies widely between calls • Compiler cannot optimize over method calls • Requires lotsLOTSof method calls • But good design emphasizes calling methods • Cannot say this is bad in other cases • Why are recursive method calls bad?
Why Not Use Recursion? • Often means values computed repeatedly • Cannot store interim results between calls • Short, simple methods limit optimizations • Behavior varies widely between calls • Compiler cannot optimize over method calls • Requires lotsLOTSTONSof method calls • But good design emphasizes calling methods • Cannot say this is bad in other cases • Why are recursive method calls bad?
Method Call Overhead • Every method call has some overhead • Load address of method to be called • Allocate stack memory for method • Push parameters onto a stack • Push return value from stack • Reload return address from the stack • Deallocate stack memory
Pain of Recursion • Compiler optimizes much of method call • Most become only 1 instruction • Leaves allocating stack space as main cost • Program starts by allocating huge stack • Then optimizes assuming stack is immobile • But lots of concurrent method calls will fill this • Rarely code many nested calls…
Pain of Recursion • Compiler optimizes much of method call • Most become only 1 instruction • Leaves allocating stack space as main cost • Program starts by allocating huge stack • Then optimizes assuming stack is immobile • But lots of concurrent method calls will fill this • Rarely code many nested calls…except when using recursion
QuickSort Results • Code same QuickSort algorithm 2 ways: • Recursively • Iteratively • Computed average of 10 trials for 3 data sizes • Increasing size also increases chance of crashing
What Can We Do? • Convert algorithm to use tail-recursion • Make recursive call as last step of recursion • Should directly return result of recursive call public int factorial(inti) { if (i <= 1) { return 1; } else {int result = factorial(i – 1); return result * i; }}
What Can We Do? • Convert algorithm to use tail-recursion • Make recursive call as last step of recursion • Should directly return result of recursive call public int factorial(inti) { if (i <= 1) { return 1; } else { return i * factorial(i – 1); }}
What Can We Do? • Convert algorithm to use tail-recursion • Make recursive call as last step of recursion • Should directly return result of recursive call public int factorial(inti, int fact) { if (i <= 1) { return fact; } else { return factorial(i – 1, fact * i); }}
Tail Recursion • Conversion of tail-recursion to iteration easy • Create local variable to track most recent result • Recreate recursionusing for or while loop • Get benefits of simplicity without the costs public int factorial(inti, int fact) { if (i <= 1) { return fact; } else { return factorial(i – 1, fact * i); }}
Tail Recursion • Conversion of tail-recursion to iteration easy • Create local variable to track most recent result • Recreate recursionusing for or while loop • Get benefits of simplicity without the costs public int factorial(inti) {int fact; if (i <= 1) { // Stop recursion } else {fact *= i; // Continue recursion }}
Tail Recursion • Conversion of tail-recursion to iteration easy • Create local variable to track most recent result • Recreate recursionusing for or while loop • Get benefits of simplicity without the costs public int factorial(inti) {int fact = 1; while (i > 1) { fact *= i;i--; } return fact;}
Recursion Elimination • Must first have code that meets criteria • Only for tail recursion (recursion at end) • Works when methodreturns recursive result • Often can rewrite code to meet these needs • Once done, simple to remove recursion • Add local variable storing result as we go • Uses foror whileloop to replace recursion • Easier to optimize code & saves call overhead
Recursion Elimination • Must first have code that meets criteria • Only for tail recursion (recursion at end) • Works when methodreturns recursive result • Often can rewrite code to meet these needs • Once done, simple to remove recursion • Add local variable storing result as we go • Uses foror whileloop to replace recursion • Easier to optimize code & saves call overhead
Tail Recursion Elimination • not all algorithms can be converted • For some algorithms, recursion may be required
Remove Recursion By Cheating • Slow performance due to stack overhead • While cannot always remove recursion • Can remove overhead’s source • Instantiate Stack at beginning of method • Program Stackreplaced with local Stack • Method iterateswhile Stack is not empty • Start loop by popping value to be processed
Remove Recursion By Cheating public void printInOrder(BNode<T> root) {if (root.hasLeft()) {printInOrder(root.leftChild());}System.out.println(root.element());if (root.hasRight()) {printInOrder(root.rightChild());} }
Remove Recursion By Cheating • public void printInOrder(BNode<T> root) {Stack stack = // Instantiate stackstack.push(root);while (!stack.isEmpty()) { Object node = stack.pop();if (node.hasRight()) { push(node.rightChild()); }push(node.element());if (node.hasLeft()) { push(node.leftChild()); }} }
Remove Recursion By Cheating • public void printInOrder(BNode<T> root) {Stack stack = // Instantiate stackstack.push(root);while (!stack.isEmpty()) { Object node = stack.pop();if (node instanceofBNode) {if (node.hasRight()) { push(node.rightChild()); }push(node.element());if (node.hasLeft()) { push(node.leftChild()); }} else { System.out.println(node); }} }
Cheater Removing Recursion • Use local Stack to replace program Stack • Stack handles multiple data types • Pop value off Stack with each iteration • Loop’s actions depend on value’s type • Compiler can now optimize this method • Stackmethods highly optimized • Growing & shrinking this Stack much easier • Single call means better chance of optimizing
Recursion Elimination • To do this, algorithm must be tail recursive • Rewrite code, if possible, before starting • Conversion of tail-recursion to iteration easy • Create local variable to store current result • Use for or while loop to recreate recursion • Use recursive stepas body of the loop • Upon reaching base case should terminate loop • After loop code from base case is performed
For Next Lecture • Lab #3 due before lab time Friday • Asks you to implement Observer Pattern • Read pages 109 – 122 of the book • Within a program, how do you instantiate objects? • Any design principles not violated by new? • Haven’t we already used Pizza in a pattern?