Presentation is loading. Please wait.

Presentation is loading. Please wait.

1 CS 163 Data Structures Chapter 6 Function Calls and Recursion Herbert G. Mayer, PSU Status 6/1/2015.

Similar presentations


Presentation on theme: "1 CS 163 Data Structures Chapter 6 Function Calls and Recursion Herbert G. Mayer, PSU Status 6/1/2015."— Presentation transcript:

1 1 CS 163 Data Structures Chapter 6 Function Calls and Recursion Herbert G. Mayer, PSU Status 6/1/2015

2 2 Syllabus C++ Functions C++ Functions Calls Calls Varying Number of Actuals Varying Number of Actuals Nested Calls Nested Calls Recursion Recursion Arithmetic Ops via Recursion

3 3 C++ Functions Functions are prime building blocks for C++ programs that render them readable and maintainable; named logical modules Functions are prime building blocks for C++ programs that render them readable and maintainable; named logical modules A function is a contained module, identified by its name A function is a contained module, identified by its name That name can be called, in which case the function executes, regardless of where in the program it appears textually That name can be called, in which case the function executes, regardless of where in the program it appears textually It has been designed to enclose some logically contained and coherent purpose. That purpose is fulfilled by the call It has been designed to enclose some logically contained and coherent purpose. That purpose is fulfilled by the call It is possible to re-use a function at a different place, call it from that different place It is possible to re-use a function at a different place, call it from that different place

4 4 C++ Functions In the function declaration we refer to them as formal parameters In the function declaration we refer to them as formal parameters The call provides compatible actual parameters The call provides compatible actual parameters Actual and formal must pair-wise match in type and correspond by position: type compatibility Actual and formal must pair-wise match in type and correspond by position: type compatibility Like in C, it is allowed to pass a smaller number of actual parameters than formally specified Like in C, it is allowed to pass a smaller number of actual parameters than formally specified A void function solely performs the action of the statements enclosed in the { and } pair and then returns A void function solely performs the action of the statements enclosed in the { and } pair and then returns A true function, however, may return a value to its place of call; the type is specified in the definition A true function, however, may return a value to its place of call; the type is specified in the definition

5 5 Varying Number of Actuals! Define void function foo( int a, int b) with 2 formal parameters Define void function foo( int a, int b) with 2 formal parameters If a is 0, a message stating so is printed, and parameter b is skipped, i.e. there will be no corresponding actual If a is 0, a message stating so is printed, and parameter b is skipped, i.e. there will be no corresponding actual But if a is greater than 0, the value of the second, b is printed But if a is greater than 0, the value of the second, b is printed Note: Some compilers do not allow by default Note: Some compilers do not allow by default

6 6 Implement Varying Number of Actuals #include #include void foo( int a, int b ) { // foo if ( a ) { cout << “parameter b = “ << b << endl; }else{ cout << “no value for b is passed” << endl; } //end if } //end foo int main( void ) { // main foo( 0 ); foo( 1, 2014 ); return 0; } //end main

7 7 Discuss Varying Number of Actuals It would be an error, if a smaller number of actuals than specified is passed, and yet such an uninitialized formal parameter would be referenced that has not actually been provided It would be an error, if a smaller number of actuals than specified is passed, and yet such an uninitialized formal parameter would be referenced that has not actually been provided In such cases, random garbage on the run- time stack would be mis-interpreted as formal parameter b In such cases, random garbage on the run- time stack would be mis-interpreted as formal parameter b Generally a serious error  Generally a serious error 

8 8 Function min( a, b ) Define int function min() that returns the smaller of 2 passed actual parameters Define int function min() that returns the smaller of 2 passed actual parameters Though trivial, we extend this to allow the selection of the smallest of 3 or more candidate values Though trivial, we extend this to allow the selection of the smallest of 3 or more candidate values One way to achieve this is to nest some of the actual parameters via further function calls One way to achieve this is to nest some of the actual parameters via further function calls

9 9 Implement min( a, b ) #include #include int min( int a, int b ) { // min return ( a < b ) : a ? b; } //end foo int main( void ) { // main cout << “smaller of -12, 12:” << min( -12, 12 ) << endl; << min( -12, 12 ) << endl; cout << “smallest of 88, -9, 100:” << min( min( 88, -9 ), 100 ) << endl; << min( min( 88, -9 ), 100 ) << endl; cout << “smallest of 200, 300, 400:” << min( 200, min( 300, 400 ) ) << endl; << min( 200, min( 300, 400 ) ) << endl; cout << “smallest of 10, -99, 100, -888:” << min( min( 10, -99 ), min( 10, -888 ) ) << endl; << min( min( 10, -99 ), min( 10, -888 ) ) << endl; return 0; } //end main

10 10 Discuss min( a, b ) => min( a, b, c, d ) The point here is to demonstrate nested function calls The point here is to demonstrate nested function calls Allows virtual extension of a function to a more complex one, without having to code it Allows virtual extension of a function to a more complex one, without having to code it See 2 samples of min( a, b, c ) with 3 candidates See 2 samples of min( a, b, c ) with 3 candidates And 1 sample of selecting the smallest of 4 candidates, and yet we have implemented just a simple min( a, b ) function And 1 sample of selecting the smallest of 4 candidates, and yet we have implemented just a simple min( a, b ) function

11 11 Definition of Recursive Algorithm An algorithms is recursive, if it is partly defined by simpler versions of itself [1] A recursive program is the implementation of a recursive algorithm What is the key problem for a programmer, using a language that is non-recursive (e.g. standard Fortran) if the algorithm to be implemented is recursive? --See later! What then are the other parts of a recursive algorithm? Correct recursive algorithm requires a starting point, formally known as “base case” Base case could be multiple steps Recursive algorithm a() uses a base case as starting point for computation, plus the actual function body, including some recursive use of a() Recursive body can be indirectly recursive through intermediate function a()-> b()-> a() – through intermediate function b() Primitive examples are the factorial( n ) function; or Fibonacci( n ), for non-negative arguments n; Fibo( n ) shown here: Base case 1:Fibo(0) = 0 Base case 2:Fibo(1) = 1 Recursive Definition:Fibo( n ) for n > 1 = Fibo( n-1 ) + Fibo( n-2 )

12 12 Function Fibo( n ) Define int function Fibo() that returns the Fibonacci number of its passed, unsigned, integer argument n Define int function Fibo() that returns the Fibonacci number of its passed, unsigned, integer argument n Though trivial to code iteratively, we use recursion to compute Fibo( n ) Though trivial to code iteratively, we use recursion to compute Fibo( n ) We saw the definition for recursion earlier, so we know: We saw the definition for recursion earlier, so we know: Check for a termination condition; that is the “partly defined” condition Check that the recursive call proceeds with a simpler argument than the original; that is the “simpler version” of the recursion definition Let us ignore that integers could be negative, and assume non-negative, original arguments to Fibo() Let us ignore that integers could be negative, and assume non-negative, original arguments to Fibo()

13 13 Implement Fibo( n )... int Fibo( unsigned n ) { // Fibo if ( 0 == n ) {// why 0 first? return 0; }else if ( 1 == n ) {// why 1 first? return 1; }else{ return Fibo( n – 1 ) + Fibo( n – 2 ); } //end Fibo int main( void ) { // main cout << “Fibo( 8 ) = “ << Fibo( 8 ) << endl; return 0; } //end main

14 14 Discuss Fibo( n ) Demonstrate recursive function calls Demonstrate recursive function calls But the recursive function is defined in simpler versions of itself, hence we cannot possibly call Fibo( n ) in the body But the recursive function is defined in simpler versions of itself, hence we cannot possibly call Fibo( n ) in the body However: Fibo( n-1 ), and also Fibo( n-2 ) are valid possibilities, they are simpler calls However: Fibo( n-1 ), and also Fibo( n-2 ) are valid possibilities, they are simpler calls Also, the recursive function is partly defined via a call to itself; i.e. there are other parts Also, the recursive function is partly defined via a call to itself; i.e. there are other parts Those parts are the checks for termination early: is the argument already 0 or 1, if so, we know and return the result. Those parts are the checks for termination early: is the argument already 0 or 1, if so, we know and return the result. In all other cases: recurse! In all other cases: recurse!

15 15 Iterative Fibo( n ) // unsigned, no need to check for negative! unsigned fibo1( unsigned arg ) { // fibo1 unsigned fm2 = 0; unsigned fm1 = 1; unsigned fm0 = 1; for ( int i = 1; i <= arg; i++ ) { fm2 = fm1; fm1 = fm0; fm0 = fm1 + fm2; } //end for return fm2; } //end fibo1

16 16 Q-Sequence, Definition Q-Sequence defined by Douglas Hofstadter in [1] as a function q( n ) for positive integers n > 0 Base case n = 1: q(1) = 1 Base case n = 2: q(2) = 1 Recursive definition of q(n), for positive n > 2 q( n ) = q( n – q( n - 1 ) ) + q( n – q( n - 2 ) ) Q-Sequence reminds us of Fibonacci( n ) function, but with surprising difference in the type of result: By contract, the function results of fibonacci( n ) are monotonically increasing with increasing argument Results of q( n ) are non-monotonic! Note # of calls: calls(q( 40 )) = 1,137,454,741

17 17 Q-Sequence, Coded in C #define MAX 100// arbitrary limit; never reached!!!! int calls;// will be initialized each time int q( int arg ) { // q calls++;// track another call if ( arg <= 2 ) { return 1;// base case return 1;// base case }else{// now recurse! return q( arg - q( arg-1 ) ) + q( arg - q( arg-2 ) ); return q( arg - q( arg-1 ) ) + q( arg - q( arg-2 ) ); } // end if } // end q // note: printf() allowed in C++ void main() { // main for( int i = 1; i < MAX; i++ ) { calls = 0;// initially no calls yet calls = 0;// initially no calls yet printf( "Q(%2d) = %3d, #calls = %10d\n", i, q(i), calls ); printf( "Q(%2d) = %3d, #calls = %10d\n", i, q(i), calls ); } // end for } // end main

18 18 Q-Sequence Results Q( 1) = 1, #calls = 1 Q( 2) = 1, #calls = 1 Q( 3) = 2, #calls = 5 Q( 4) = 3, #calls = 13 Q( 5) = 3, #calls = 25 Q( 6) = 4, #calls = 49 Q( 7) = 5, #calls = 93 Q( 8) = 5, #calls = 161 Q( 9) = 6, #calls = 281 Q(10) = 6, #calls = 481 Q(11) = 6, #calls = 813...... Q(26) = 14, #calls = 1341433 Q(27) = 16, #calls = 2174493 Q(28) = 16, #calls = 3521137 Q(29) = 16, #calls = 5700281 Q(30) = 16, #calls = 9229053 Q(31) = 20, #calls = 14941993 Q(32) = 17, #calls = 24182797 Q(33) = 17, #calls = 39137473 Q(34) = 20, #calls = 63354153 Q(35) = 21, #calls = 102525697 Q(36) = 19, #calls = 165896537 Q(37) = 20, #calls = 268460333 Q(38) = 22, #calls = 434429737 Q(39) = 21, #calls = 702952137 Q(40) = 22, #calls = 1137454741... Will never reach Q(100) in your life time... Will never reach Q(100) in your life time

19 19 Ackermann Definition Ackermann a( m, n ) is defined as a function of two non- negative integers m and n Base case 1: a( 0, n ) = n + 1 Base case 2: a( m, 0 ) = a( m - 1, 1 ) Recursive definition of a( m, n ), m, n > 0 a( m, n ) = a( m - 1, a( m, n - 1 ) ) Ackermann complexity grows awfully fast; e.g. a(4,2) is an integer number with 19,729 decimal digits; greater than the national US debt!

20 20 Ackermann Definition Students, code now in C++, and volunteers shows result on white-board: Base case 1: a( 0, n ) = n + 1 Base case 2: a( m, 0 ) = a( m - 1, 1 ) Recursive definition of a( m, n ), m, n > 0 a( m, n ) = a( m - 1, a( m, n - 1 ) )

21 21 Ackermann Coded in C unsigned a( unsigned m, unsigned n ) { // a calls++;// global unsigned if ( 0 == m ) {// note operand order return n + 1;// first base case return n + 1;// first base case }else if ( 0 == n ) {// m > 0 return a( m - 1, 1 );// other base case return a( m - 1, 1 );// other base case }else{// m > 0, n > 0 return a( m-1, a( m, n-1 ) );// recurse! return a( m-1, a( m, n-1 ) );// recurse! } // end if } // end q void main() { // main for( int i = 0; i < MAX; i++ ) { printf( "\nFor m = %d\n", i ); printf( "\nFor m = %d\n", i ); for( int j = 0; j < MAX; j++ ) { for( int j = 0; j < MAX; j++ ) { calls = 0; calls = 0; printf( "a(%1d,%1d) = %10u, calls = %12u\n", printf( "a(%1d,%1d) = %10u, calls = %12u\n", i, j, a( i, j ), calls ); i, j, a( i, j ), calls ); } // end for } // end for } // end for } // end main

22 22 Ackermann Results For m = 0 a(0,0) = 1, calls = 1... For m = 1... a(1,7) = 9, calls = 16 For m = 2 a(2,0) = 3, calls = 5 a(2,1) = 5, calls = 14 a(2,2) = 7, calls = 27 a(2,3) = 9, calls = 44 a(2,4) = 11, calls = 65 a(2,5) = 13, calls = 90 a(2,6) = 15, calls = 119 a(2,7) = 17, calls = 152 For m = 3 a(3,0) = 5, calls = 15 a(3,1) = 13, calls = 106 a(3,2) = 29, calls = 541 a(3,3) = 61, calls = 2432 a(3,4) = 125, calls = 10307 a(3,5) = 253, calls = 42438 a(3,6) = 509, calls = 172233 a(3,7) = 1021, calls = 693964 For m = 4 a(4,0) = 13, calls = 107 don’t even dream about computing a(4,2) or higher!

23 23 Recursion vs. Iteration Iteration is expressed in programming languages by loops; e.g. for-, while-, do-, or repeat loopsIteration is expressed in programming languages by loops; e.g. for-, while-, do-, or repeat loops These are readable and efficient methods for expressing iteration, but are not strictly necessaryThese are readable and efficient methods for expressing iteration, but are not strictly necessary Recursion can replace iteration; yet for some people this seems counter-intuitiveRecursion can replace iteration; yet for some people this seems counter-intuitive Neophytes are sometimes unused to recursion; yet recursion can be as intuitive as simple iterationNeophytes are sometimes unused to recursion; yet recursion can be as intuitive as simple iteration

24 24 Arithmetic Ops via Recursion Following experiment is to simulate mathematical operations via recursionFollowing experiment is to simulate mathematical operations via recursion We’ll now recode every mathematical function via recursion, except we allow:We’ll now recode every mathematical function via recursion, except we allow: Using only functions, called recursivelyUsing only functions, called recursively Plus arithmetic increment/decrement operators ++ -- and unary minus –Plus arithmetic increment/decrement operators ++ -- and unary minus – And conventional relational operators, such as > >= != etc.And conventional relational operators, such as > >= != etc. All other operators are dis-allowed in this experiment, i.e. no + - * / % etc.All other operators are dis-allowed in this experiment, i.e. no + - * / % etc.

25 25 Arithmetic Ops via Recursion: add() // simulate addition via recursion: // return a + b without + operation! int add( int a, int b ) { // add if ( 0 == b ) { return a; }else if ( b < 0 ) { return add( --a, ++b ); }else{ return add( ++a, --b ); } //end if } //end add

26 26 Arithmetic Ops via Recursion: sub() // simulate subtraction via recursion: // return a – b; no dyadic – operation int sub( int a, int b ) { // sub return add( a, -b ); } //end sub

27 27 Arithmetic Ops via Recursion: mult() // return a * b, no * but add() int mult( int a, int b ) { // mult if ( 0 == b ) { return 0; }else if ( 1 == b ) { return a; }else if ( b < 0 ) { return -mult( a, -b ); }else{ // b > 0 return add( a, mult( a, --b ) ); } //end if } //end mult

28 28 Arithmetic Ops via Recursion: expo() // return a ** b, no ** op in C++; requires mult( int, int ) int expo( int a, int b ) { // expo if ( 0 == a ) { if ( 0 == a ) { if ( 0 == b ) { if ( 0 == b ) { cout << ”undefined value0^0” << endl; cout << ”undefined value0^0” << endl; }else if ( b < 0 ) { }else if ( b < 0 ) { cout << “0 to <0 power undefined” << endl; cout << “0 to <0 power undefined” << endl; } //end if } //end if return 0; return 0; }else if ( 0 == b ) { }else if ( 0 == b ) { return 1; return 1; }else if ( 1 == a ) { }else if ( 1 == a ) { return 1; return 1; }else if ( -1 == a ) { }else if ( -1 == a ) { return b % 2 ? -1 : 1; return b % 2 ? -1 : 1; }else if ( b < 0 ) { }else if ( b < 0 ) { return 0; return 0; }else{ }else{ return mult( expo( a, --b ), a ); return mult( expo( a, --b ), a ); } //end if } //end if } //end expo

29 29 Replace Iteration via Recursion Simulation of Recursion in non-recursive programming languages, by using an explicit stack and thus simulating a run-time stack, is pretty advanced for CS 163! Simulation of Recursion in non-recursive programming languages, by using an explicit stack and thus simulating a run-time stack, is pretty advanced for CS 163! Let’s learn this!


Download ppt "1 CS 163 Data Structures Chapter 6 Function Calls and Recursion Herbert G. Mayer, PSU Status 6/1/2015."

Similar presentations


Ads by Google