CS 162
Introduction to Computer Science
Chapter 4
Function Calls
Herbert G. Mayer, PSU
Status 11/9/2014
1
Syllabus
C++ Functions
Calls
Varying Number of Actuals
Nested Calls
Recursion
2
C++ Functions
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
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 is possible to re-use a function at a different place,
call it from that different place
3
C++ Functions
In the function declaration we refer to them as
formal parameters
The call provides compatible actual parameters
Actual and formal must pairwise match in type and
correspond by position: type compatibulity
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
A true function, however, may return a value to its
place of call; the type is specified in the definition
4
Varying Number of Actuals
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
But if a is greater than 0, the value of the
second, b is printed
Note: Some compilers do not allow by
default
5
Implement Varying Number of Actuals
#include <iostream.h>
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
6
Discuss Varying Number of Actuals
It would be an error, if a smaller than
specified number of actual is passed, and
yet such a formal parameter would be
referenced that has not actually been
provided
In such cases, random garbage on the runtime stack is mis-interpreted as formal ‘b’
Generally a serious error
7
Function min( a, b )
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
One way to achieve this is to nest some of
the actual parameters via further function
calls
8
Implement min( a, b )
#include <iostream.h>
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;
cout << “smallest of 88, -9, 100:”
<< min( min( 88, -9 ), 100 ) << endl;
cout << “smallest of 200, 300, 400:”
<< min( 200, min( 300, 400 ) ) << endl;
cout << “smallest of 10, -99, 100, -888:”
<< min( min( 10, -99), min( 10, -888 ) ) << endl;
return 0;
} //end main
9
Discuss min( a, b ) => min( a, b, c, d )
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
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
10
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 )
11
Function Fibo( 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 )
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()
12
Implement Fibo( n )
. . .
int Fibo( unsigned n )
{ // Fibo
if ( 0 == n ) {
return 0;
}else if ( 1 == n ) {
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
13
Discuss Fibo( n )
Demonstrate recursive function calls
But the recursive function is defined in
simpler versions of itself, hence we cannot
call Fibo( n ) any longer in the body
So: Fibo( n-1 ), and also Fibo( n-2 ) are valid
possibilities
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.
In all other cases: recurse!
14
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
15
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++;
//
if ( arg <= 2 ) {
return 1;
//
}else{
//
return q( arg } // end if
} // end q
track another call
base case
now recurse!
q( arg-1 ) ) + q( arg - q( arg-2 ) );
// note: printf() allowed in C++
void main()
{ // main
for( int i = 1; i < MAX; i++ ) {
calls = 0;
// initially no calls yet
printf( "Q(%2d) = %3d, #calls = %10d\n", i, q(i), calls );
} // end for
} // end main
16
Q-Sequence Results
Q( 1)
Q( 2)
Q( 3)
Q( 4)
Q( 5)
Q( 6)
Q( 7)
Q( 8)
Q( 9)
Q(10)
Q(11)
=
=
=
=
=
=
=
=
=
=
=
1,
1,
2,
3,
3,
4,
5,
5,
6,
6,
6,
#calls
#calls
#calls
#calls
#calls
#calls
#calls
#calls
#calls
#calls
#calls
=
=
=
=
=
=
=
=
=
=
=
1
1
5
13
25
49
93
161
281
481
813
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
14,
16,
16,
16,
16,
20,
17,
17,
20,
21,
19,
20,
22,
21,
22,
#calls
#calls
#calls
#calls
#calls
#calls
#calls
#calls
#calls
#calls
#calls
#calls
#calls
#calls
#calls
=
1341433
=
2174493
=
3521137
=
5700281
=
9229053
=
14941993
=
24182797
=
39137473
=
63354153
= 102525697
= 165896537
= 268460333
= 434429737
= 702952137
= 1137454741
. . .
Q(26)
Q(27)
Q(28)
Q(29)
Q(30)
Q(31)
Q(32)
Q(33)
Q(34)
Q(35)
Q(36)
Q(37)
Q(38)
Q(39)
Q(40)
. . . Will never reach Q(100) in your life time
17
Ackermann Definition
Ackermann a( m, n ) is defined as a function of two nonnegative 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!
18
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 ) )
19
Ackermann Coded in C
unsigned a( unsigned m, unsigned n )
{ // a
calls++;
if ( 0 == m ) {
return n + 1;
}else if ( 0 == n ) {
return a( m - 1, 1 );
}else{
return a( m-1, a( m, n-1 ) );
} // end if
} // end q
//
//
//
//
//
//
//
global unsigned
note operand order
first base case
m > 0
other base case
m > 0, n > 0
recurse!
void main()
{ // main
for( int i = 0; i < MAX; i++ ) {
printf( "\nFor m = %d\n", i );
for( int j = 0; j < MAX; j++ ) {
calls = 0;
printf( "a(%1d,%1d) = %10u, calls = %12u\n",
i, j, a( i, j ), calls );
} // end for
} // end for
} // end main
20
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) =
a(2,1) =
a(2,2) =
a(2,3) =
a(2,4) =
a(2,5) =
a(2,6) =
a(2,7) =
3, calls =
5, calls =
7, calls =
9, calls =
11, calls =
13, calls =
15, calls =
17, calls =
5
14
27
44
65
90
119
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!
21
Recursion vs. Iteration
• Iteration 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
necessary
• Recursion 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 iteration
22
Replace Iteration via Recursion
• Using only functions, called recursively
• Plus arithmetic increment/decrement
operators ++ -- and unary minus –
• And conventional relational operators > >=
!= etc.
• All other operators are dis-allowed in this
experiment, i.e. no + - * / % ** etc.
23
Recursion vs. Iteration: add()
// 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
24
Recursion vs. Iteration: sub()
// return a – b; no dyadic – operation
int sub( int a, int b )
{ // sub
return add( a, -b );
} //end sub
25
Recursion vs. Iteration: 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
26
Recursion vs. Iteration: expo()
// return a ** b, no ** op in C++; requires mult( int, int )
int expo( int a, int b )
{ // expo
if ( 0 == a ) {
if ( 0 == b ) {
cout << ”undefined value0^0” << endl;
}else if ( b < 0 ) {
cout << “0 to <0 power undefined” << endl;
} //end if
return 0;
}else if ( 0 == b ) {
return 1;
}else if ( 1 == a ) {
return 1;
}else if ( -1 == a ) {
return b % 2 ? -1 : 1;
}else if ( b < 0 ) {
return 0;
}else{
return mult( expo( a, --b ), a );
} //end if
} //end expo
27
© Copyright 2026 Paperzz