0% found this document useful (0 votes)
33 views27 pages

7 - Functions by IITB

The document discusses functions in C++. It explains that functions allow code to be reused by defining reusable blocks of code that take parameters and return values. This reduces redundancy. An example shows defining a function to calculate a value (f(x)) that is called multiple times in the bisection method, rather than rewriting the calculation each time. Function syntax and concepts like parameter passing, return values, and activation frames are also covered.

Uploaded by

Jay Aherkar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
33 views27 pages

7 - Functions by IITB

The document discusses functions in C++. It explains that functions allow code to be reused by defining reusable blocks of code that take parameters and return values. This reduces redundancy. An example shows defining a function to calculate a value (f(x)) that is called multiple times in the bisection method, rather than rewriting the calculation each time. Function syntax and concepts like parameter passing, return values, and activation frames are also covered.

Uploaded by

Jay Aherkar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 27

Functions

Ajit Rajwade
Refer: Chapter 9 of the book by Abhiram Ranade
Revisiting our bisection method code
● We considered a function f(x) = x^3-5.
● We had to evaluate the function for many different values: xL, xR, xM.
● We had to each time write the same formula with xL, xR, xM respectively.
● See next slide which contains the code.
● This may seem okay, but consider what would happen if f(x) were a much
more complicated function, potentially requiring conditional statements if
different formulae were applied to different portions of the domain.
● Moreover, you could have needed to use this formula not just 3 times, but say
50 times.
● How do you make your code more compact?

2
Bisection method for roots of f(x)=x^3-5
main_program{
double xL = 1, xR = 2; // initial interval
double epsilon = 0.001; // precision of the root’s value
bool flagL, flagR;
double xM;
bool flagM;
flagL = xL*xL*xL-5 > 0; flagR = xR*xR*xR-5 > 0; // signs of f(xL) and f(xR)
if (flagL != flagR)
{
while (xR - xL > epsilon) // until the required precision is reached
{
xM = (xR+xL)/2; // interval midpoint
flagM = xM*xM*xM-5 > 0;
if (flagM != flagL) xR = xM; // signs of f(xM) and f(xL)
else if (flagM != flagR) xL = xM;
}
}
cout << “the root is “ xL <<;
3
}
C++ functions to the rescue
● We need to write a C++ function to compute the value of f(x) given x.
● We can call it multiple times from the main program.
● This reduces code redundancy, and reduces scope for errors.
● The function will have the following form:

double f ( double x)

● f is the name of the function and it takes parameter x of type double as input.
It also returns a value of type double.
● With this modification, the same code will look like what you will see on the
next slide.

4
double f (double x) { return x*x*x-5; } // function declaration

main_program{
double xL = 1, xR = 2; // initial interval
double epsilon = 0.001; // precision of the root’s value
bool flagL, flagR, flagM; double xM;
flagL = f(xL) > 0; flagR = f(xR) > 0; // signs of f(xL) and f(xR)
if (flagL != flagR)
{
while (xR - xL > epsilon) // until the required precision is reached
{
xM = (xR+xL)/2; // interval midpoint
flagM = f(xM) > 0;
if (flagM != flagL) xR = xM; // signs of f(xM) and f(xL)
else if (flagM != flagR) xL = xM;
}
}
cout << “the root is “ xL <<;
}
5
General function syntax
● The general syntax of a function declaration is:

typename_return_value function-name (typename1 parameter1, typename2


parameter2, …, typenameN parameterN)

Body; // includes a statement of the type return return_value;

● A function may take as input some N parameters, which may be of different data-types. N could be
equal to 0.
● The body of the function can contain a sequence of statements but it must contain a return
statement, returning a variable of type typename_return_value.
● A declared function is called from another function including main using the syntax: returnval =
function-name (parameter1, parameter2, …, parameterN); where returnval has
type typename_return_value.
6
Function for GCD
int gcd (int m, int n){
main_program{

while (m%n != 0) { int a=10, b=4, c = 39,


d=72;
int remainder = m%n;

m = n; n =
cout << gcd(a,b) << endl;
remainder; cout << gcd(c,d) << endl;
}
}
return n;

7
Function for Factorial

int factorial (int n){ main_program{


if (n == 0) return 1; int a=10, b=4;
int prod=1; cout << factorial(a) << endl;
for (int i = n; i > 0; i=i-1){ cout << factorial(b) << endl;
prod *= i;
}
}

return prod;

}
8
void functions
● A function that does not return any quantity is called a void function.
● Functions that draw shapes are often written as void functions because they
do not need to return anything. See next slide for the function void
drawPolygon (int numsides) which can be called from the main
program.
● Another example: a function which takes as input a number and which just
prints out the digits of the number.

9
void functions: examples
void drawPolygon (int numsides){ void printLastDigit(int n)

turtleSim(); {

for(int i = 0; i < num_sides; i++){ cout << n%10;

forward(50); left(360/num_sides); }

} int main(){

wait(5); printLastDigit(10);

} }

int main(){

drawPolygon (15);
Note that turtleSim() , forward(),
}
left(), wait() are all functions – in
fact they are all void functions!
10
Function execution
● Refer to the gcd function. What happens when the program comes to gcd(a,b); inside
main?
● The arguments to the call are evaluated. Here it implies just getting the values of a and b from
memory, but evaluation would be necessary if you had instead supplied expressions as
parameters to gcd, for example if you had called gcd(a+b, c+d);
● The calling function (main in this case - note that main is also a function!) gets suspended,
and will be resumed later, and at that point it will continue from the point where it was
suspended.
● The variables local to the main function are stored in area of memory, called the activation
frame.
● The called function (gcd here) gets its own activation frame, where it can store its own
variables (called local variables). In the case of gcd(int m, int n), the stack will store
local variables m and n. The values of a and b respectively will be copies of m and n in
memory locations different from those of m and n.
● The activation frame of all functions are part of a larger area of memory called the stack. We
say that the AF of the calling function is pushed onto the stack, before control passes to the
11
called function.
Function execution
● The values of a and b are copied to m and n respectively.
● Sample activation frames for main, gcd(a,b) and gcd(c,d) are shown on the next
slide.
● The body of the function is executed. Any space to be reserved later on for variables
of the function, must be given inside the activation frame. For example, the variable
remainder.
● The statements of body get executed until you encounter the return statement.
The value of return_expression (if any) is computed and sent back to the calling
function. This value is in fact copied into the appropriate variable of the calling
function. For example, for the call gcd(a,b), the value 12 is returned.
● The activation frame of the called function is destroyed, i.e. that portion of memory is
marked available for usage by other functions (also called “popped off the stack”).
● The calling function resumes from the point where it was suspended. The returned
value (if any) is used as required in this function.
● The values of the local variables of the calling function are obtained from the stack.

12
Activation frame of Activation frame of Activation frame of Activation frame of
main gcd(a,b) main gcd(a,b)

a: 36 m:36 a: 36 m:24

b: 24 n:24 b: 24 n:12

c: 99 c: 99 remainder: 12

d:47 d:47

After copying arguments At the end of the first iteration of the loop in gcd

Note: even though the values of m and n inside gcd change, the values of a and b in the
main function remain intact. This is desirable, but also has limitations, which we will see later.

You can similarly work out activation records for a few other functions, or for gcd(c,d) in the same
example.
13
Nested function calls
● Any given function can call other functions, which in turn can call more functions as well.
● In fact, main_program itself is a function. If you remove simplecpp and move to traditional
C++, the main function looks like the following:
int main (){
// body
return 0;
}
● An example of nested function calls for the bisection method is presented on the next slide.
You have function f, and you have another function which implements the bisection method,
and which is called from main.
● Note: each program must have a main function. You cannot call main from within itself or
any other function.
● The g++ compiler allows you not to return anything from main, even if it is declared as int
main () {}. This special leeway is not given to any other function.
14
double f (double x) { return x*x*x-5; } // function declaration
// for C++
double bisection_root (double xL, double xR, double epsilon){ int main (){
// initial interval is given by [xL, xR], given as input double xL = 1, xR = 2,
// precision of the root’s value is also given as input epsilon=0.001;
bool flagL, flagR, flagM; double xM; double rootval;
flagL = f(xL) > 0; flagR = f(xR) > 0; // signs of f(xL) and f(xR)
if (flagL != flagR)
rootval =
bisection_root(xL,xR,e
{
psilon);
while (xR - xL > epsilon) // until reqd. precision is reached
{ return 0;
xM = (xR+xL)/2; // interval midpoint }
flagM = f(xM) > 0;
if (flagM != flagL) xR = xM; // signs of f(xM) and f(xL) // for simplecpp
else if (flagM != flagR) xL = xM;
main_program () {
double xL = 1, xR = 2,
}
epsilon=0.001;
} double rootval;
return xL;
} rootval =
bisection_root(xL,xR,e
psilon);
} 15
Call by value: and its limitations
● All function calls we have seen so far fall under the category “call by value”.
● That is, the values of the parameters are passed on by the calling function to
the function being called.
● You saw this in the activation tables drawn out for the gcd function.
● Now consider the following function which attempts to swap the values of two
numbers a and b:
int main () {
void myswap (int a, int b){ int x = 10, y = 20;
int temp = a; a = b; b = temp; myswap(x,y);
return; cout << x <<”,”<<y;
} return 0;
}
16
Call by value: and its limitations
● What do you think will be the output of the program?
● The values of x and y will remain unchanged even after the call to myswap,
because those values were copied into the parameters a and b.
● But the values of a and b were never copied back into x and y.
● This is one limitation of call by value.
● As it stands, a function can return only one value at a time.
● What if you need to return two or more values? There is no provision for this.
● For example, if you wanted to write a function to take (x,y) coordinates of a
point as input and produce their polar coordinates.

17
To the rescue: Call by reference
● If you want changes in the function parameters made during a function call to
reflect even after the function call is over, you have to use call by reference.
● This is done by specifying an ampersand (&) before the parameter name when
the function is written out.
● When a parameter (say p) is called by reference, no separate storage for p is
allocated in the activation frame of the function.
● Instead the reference parameter directly refers to the original memory location
of that variable.
● Hence changes made to a parameter passed by reference within a function
reflect in the original variable itself.

18
Call by reference: swap
void myswap (int& a, int& b){ int main () {
int x = 10, y = 20;
int temp = a; a = b; b = temp;
return; myswap(x,y);
cout << x <<”,”<<y;
}
return 0;
// int &a, int &b is also valid
}
// syntax

Output: 20,10
If a and b were called by value, the output would
have been 10,20

19
Call by reference: Cartesian to polar
void cartesianToPolar (double x, double y, double &r, double
&theta){
r = sqrt(x*x + y*y);
theta = atan2(y/x);
}
int main (){
double x = 1.0, y = 1.0, theta, r;
cartesianToPolar (x,y,r,theta); cout << r << “ “ << theta;
}

Since r and theta are passed by reference, their values will be available inside main
even after a call to cartesianToPolar is completed.
20
Call by reference: some remarks
● The manner in which a function is called remains the same, whether or not one
or more of the function parameters were called by value or called by
reference.
● The change is only in the definition of the function, except for the following
point.
● If you had to call by value, you can use constant values (literals) as arguments.
For example, you could call a function: myswap(10,20) if the arguments of
myswap were by value.
● You cannot use such a call with literals, if those arguments were by reference.

21
Global Variables
● A global variable is a variable that is common to all functions in a program,
including main.
● It should be declared before the main function, for example as follows:
#include <iostream>
using namespace std;
int x = 10, y; double z, w = 4.5; char a, b = ‘X’, c;
int main (){
// body of main function
}
● Global variables can be used or modified inside any function.

22
Global Variables
● Consider a local variable int x; declared inside a function (say
main(), but could be any other).
● Let us suppose x is also a global variable.
● When you use x inside main, you are using the local variable x, and
not the global variable.
● If you want to use a global variable with the same name as a local
variable, you use ::x instead of x (but such name conflicts should
ideally be avoided).

23
#include <iostream>
using namespace std;
int x = 10, y; double z, y = 4.5; char a, b = ‘X’, c;
void f(){
x = x+1; // updates the global value of x
cout << x; // prints the global value of x
}
int main (){ The output of this program is 61112
int x = 5;
x = x+1; // updates the local value of x
cout << x; // prints the local value of x
f();
::x = ::x+1; // update the global value of x
cout << ::x;
} 24
Global Variables
● The use of global variables is generally discouraged, as it is bad programming
practice and makes large programs difficult to maintain.
● They should be used rarely, only when certain variables are genuinely used
across a large number of functions.
● For example const double pi = 3.142857; is a good example of a
global variable which can be used in all mathematical functions.
● Another example: double square_root_two = 1.414; can be used in
many geometrical functions.

25
Functions Example: Virahanka Numbers

● A sequence of numbers where the n-th number V = V + V , i.e.


n n-1 n-2
sum of (n-1)th and (n-2)the numbers.
● This is called a recurrence relation.
● Commonly called Fibonacci numbers, but discovered earlier by Virahanka.
● The first two Virahanka numbers are equal to 1.
● Applying the recurrence relation, we have V = V + V = 2, V = V +
3 2 1 4 3
V = 3, V = V + V = 5, and so on.
2 5 4 3
● Given the recurrence relation, we now write a function to compute the n-th
Virahanka number.
● We will study later on as to why someone thought of such a sequence of
numbers.
26
int virahanka (int n){ int main(){
int n,v;
if ( n == 1) return 1;
cout << “Which Virahanka
if (n == 2) return 1; number do you want to
calculate?”;
int prev = 1, curr = 1,val; cin >> n;
for(int i = 0; i < n-2;i++){ v = virahanka(n);
cout << “The Virahanka
val = prev + curr; number at position “ << n <<
“is “ << v;
prev = curr;
}
curr = val;
Here, we have followed the convention that the first two Virahanka
} numbers are 1,1. In some books, the convention followed is that the first
two Virahanka numbers are either 0,1 or else 1,2. The essence of the
return val; Virahanka sequence remains the same in all cases, and the code will
change very slightly.
}

27

You might also like