Lecture 12: Eliminating Recursion
description
Transcript of Lecture 12: Eliminating Recursion
LECTURE 12:ELIMINATING RECURSION
CSC 313 – Advanced Programming Topics
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 lots 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 lots LOTS TONS of 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:1. Recursively2. Iteratively
Computed average of 10 trials for 3 data sizes
Increasing size also increases chance of crashing
Algorithm
n = 100 n = 1000
n = 10000
Iterative 1.0 1.0 1.0Recursiv
e 1.4 1.3 1.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
callpublic int factorial(int i) { if (i <= 1) { return 1; } else { int result = factorial(i – 1); return result * i; }}
public int factorial(int i) { 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(int i, int fact) { if (i <= 1) { return fact; } else { return factorial(i – 1, fact * 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
Tail Recursion
Conversion of tail-recursion to iteration easy Create local variable to track most
recent result Recreate recursion using for or while
loop Get benefits of simplicity without the
costs
public int factorial(int i, int fact) { if (i <= 1) { return fact; } else { return factorial(i – 1, fact * i); }}
public int factorial(int i) { 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 recursion using for or while
loop Get benefits of simplicity without the
costs
public int factorial(int i) { int fact = 1; while (i > 1) { fact *= i; i--; } return fact;}
Tail Recursion
Conversion of tail-recursion to iteration easy Create local variable to track most
recent result Recreate recursion using for or while
loop Get benefits of simplicity without the
costs
Recursion Elimination
Must first have code that meets criteria Only for tail recursion (recursion at end) Works when method returns 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 for or while loop 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 method returns 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 for or while loop 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 Stack replaced with local Stack
Method iterates while Stack is not empty Start loop by popping value to be
processed
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 instanceof BNode) { if (node.hasRight()) { push(node.rightChild()); } push(node.element()); if (node.hasLeft()) { push(node.leftChild()); } } else { System.out.println(node); }}
}
Remove Recursion By Cheating
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 Stack methods 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 easy1. Create local variable to store current
result2. Use for or while loop to recreate
recursion Use recursive step as 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?