1 Joe Meehean
call themselves directly or indirectly void f(){... f();... } void g(){... h();... } void h(){... g();... }
int factorial(int x){ int handback; if( x <= 1 ){ handback = 1; }else{ handback = x * factorial(x-1); } return handback; } factorial(4) = 4 * factorial(4 – 1) factorial(3) = 3 * factorial(3 – 1) … factorial(1) = 1
When a recursive call is made: marks current place in code clones itself copy of code new parameters new set of uninitialized local variables clone executes starting at beginning When clone returns clone is cleaned up previous version starts at just after recursive method call
CLASS ACTIVITY void doClap(int k){ if(k == 0 ) return; clap k times; doClap(k – 1); return; } 1 st person: doClap(3)
doClap(3) 3, 4, 5, 6, … memory access error stack overflow need code to stop recursion eventually void doClap(int k){ clap k times; doClap(k + 1); return; }
Must have a base case Condition under which no recursive call is made Helps prevent infinite recursion void doClap(int k){ if( k == 0 ) return; clap k times; doClap(k + 1); return; } Still has infinite recursion?
Must make progress towards base case void doClap(int k){ if( k == 0 ) return; clap k times; doClap(k - 1); return; } Does doClap follow rules 1 & 2? Infinite recursion? If so, how should we change the code
Must make progress towards base case void doClap(int k){ if( k <= 0 ) return; clap k times; doClap(k - 1); return; } Does doClap follow rules 1 & 2? Infinite recursion? If so, how should we change the code
printStr(“hello”, 0) => “hello” printStr(“hello”, 2) => “llo” void printStr(string s, int k){ if( k >= s.length() ) return; cout << s[k]; printStr(s, k+1); }
Design rule assume that all recursive calls work even if you haven’t finished writing it yet void printStr(string s, int k){ if( k >= s.length() ) return; cout << s[k]; printStr(s, k+1); }
void printStr(string s, int k){ if( k >= s.length() ) return; cout << s[k] << endl; printStr(s, k+1); } cloneoutput printStr(“hello”, 0) printStr(“hello”, 1) printStr(“hello”, 2) printStr(“hello”, 3) printStr(“hello”, 4) printStr(“hello”, 5) h e l l o
void printStr(string s, int k){ if( k >= s.length() ) return; printStr(s, k+1); cout << s[k] << endl; } cloneoutput printStr(“hello”, 0) printStr(“hello”, 1) printStr(“hello”, 2) printStr(“hello”, 3) printStr(“hello”, 4) printStr(“hello”, 5) h e l l o
A method may have statements before recursive call after recursive both After statements are done only when the recursive call is finished and all the recursive calls recursive calls have finished
Whats printed for printStr(“STOP”, 0)? void printStr(string s, int k){ if( k >= s.length() ) return; cout << s[k] << endl; printStr(s, k+1); cout << s[k] << endl; }
Whats printed for printStr(“STOP”, 0)? STOPPOTS How can we change it to print STOP POTS void printStr(string s, int k){ if( k >= s.length() ) return; cout << s[k] << endl; printStr(s, k+1); cout << s[k] << endl; }
How can we change it to print STOP POTS void printStr(string s, int k){ if( k >= s.length() ){ cout ” << endl; return; } cout << s[k] << endl; printStr(s, k+1); cout << s[k] << endl; }
18
CallValue of k mystery(3)6 mystery(2)4 mystery(1)2 mystery(0) void mystery(int j){ if( j <= 0 ) return; int k = j * 2; mystery(j – 1); cout << k << “ “; } Each clone gets own local variables Outputs => 2 4 6
// sums #s from 1 to max // Iterative version int doSum(int max){ int sum = 0; for(int i = 1; i <= max; i++){ sum += i; } return sum; }
// sums #s from 1 to max // Iterative version int doSum(int max){ int sum = 0; for(int i = 1; i <= max; i++){ sum += i; } return sum; } // Recursive version int doSum(int max){ if( max <= 0 ) return 0; return max + doSum(max – 1); }
doSum(3) doSum(2) doSum(1) doSum(0)
Can do recursion with int parameters count up or down to base case Or, with recursive data structures a linked list is either empty or a node followed by a linked list
Prints list in order void printList(Node * node){ if( node == NULL ) return; cout data_ << endl; printList(node->next_); }
How can we change this code to print list backwards? void printList(Node * node){ if( node == NULL ) return; cout data_ << endl; printList(node->next_); }
void printList(Node * node){ if( node == NULL ) return; printList(node->next_); cout data_ << endl; } Prints list backwards Rare case where recursion makes code faster as well as simpler
Activation records (ARs) stack of ARs maintained at runtime 1 AR for each active method active methods have been called, but not returned yet Each AR includes: function parameters local variables return address
Activation Record Stack when method is called, its AR is pushed when method returns, its AR is popped return address in popped AR tells program where to return control flow
Auxiliary recursion wrapper method to do something ONCE before or after recursion OR pass more parameters recursion in auxiliary method wrapper sometimes called a driver Multiple recursive calls per method need to recurse down a few different paths more on this when we talks about trees
// prints a header and the endline // calls aux to print the list void printList(Node * node){ cout << “The list contains: “; printAux(node); cout << endl; } // prints a single string containing // each element in brackets void printAux(Node * node){ if( node == NULL ) return; cout data_ << “]”; printAux(node->next_); }
// public method calls private method // with member variable int List::numNodes(){ return numAux(phead_); } // actually counts the nodes int List::numAux(Node * node){ if( node == NULL ){ return 0; } return 1 + numAux(node->next_); }
Sum all elements in a vector but the first value int weirdSum(vector & intVect){ return sumVector(intVect, 1); } // actually sums the entries int sumVector( vector & intVect, int pos){ if( pos >= intVect.size() ) return 0; return intVect[pos] + sumAux(intVect, pos + 1); }
Fibonacci fib(1) = 1, fib(2) = 1 fib(N) for N > 2 = fib(N-1) + fib(N-2) fib(3) = fib(2) + fib(1) = = 2 int fib(int n){ if( n <= 2) return 1; return fib(n – 2) + fib(n – 1); }
fib(4) fib(3) fib(2) fib(1) Note: fib(2) is called twice Redoing work
Compound interest rule Never duplicate work don’t solve the same recursive instance in separate recursive calls repeats work you’ve already done more on how to prevent this in CS242
36
Recursion DOES NOT make your code faster Recursion DOES NOT use less memory Recursion DOES make your code simpler sometimes
When you don’t get anything from it? Tail recursion the only recursive call is the last line local variables stored just to be thrown away Tail recursion can be replaced with a while loop
void printStr(string s, int k){ if( k >= s.length() ) return; cout << s[k] << endl; printStr(s, k+1); } void printStrin(string s, int k){ while( true ){ if( k >= s.length() ) return; cout << s[k] << endl; k = k + 1; }
void printString(string s, int k){ while( k < s.length() ){ cout << s[k] << endl; k = k + 1; }
1. Base case 2. Make progress 3. Design rule 4. Compound interest rule
42