## Computing Concepts with C++ Essentials Laboratory Notebook Chapter 5 - Functions

### Cay S. Horstmann Geof Pawlicki

Once this form has been customized for your institution, you can use this button to send your lab work. Be sure to read the instructions before starting your work.

#### Lab Objectives

To gain experience in

• relating a function's parameter/return value interface to its purpose
• tracing the flow of a function's execution
• function naming and commenting
• recognizing when to use value and reference parameters
• determining the scope of variables
• decomposing complex tasks into simpler ones
• designing functions that solve practical problems
• programming recursive functions

### R1. Functions as Black Boxes

Predictable input will result in predictable output. A function maps element(s) from a problem domain, called parameter(s), into an element from a range of solutions, called the return value.

To treat a function as a "Black Box", one simply uses it.

For instance, here's a function to compute the volume of a cylinder, say, a beer can, given the height and diameter in millimeters.

```/* PURPOSE:   Function to compute the volume of a cylinder
RECEIVES:  height - the height in millimeters
diameter - the diameter in millimeters
RETURNS:   volume - in cubic milliliters
*/
float cylinder_volume(float height, float diameter)
{  . . .
}
```

To use this function to measure the volume of a can of Blatz Lite, just supply parameters and get its return.

```float v = cylinder_volume(2 * can_depth, 1.5 * can_diameter);
```

Notice that you can use the function without knowing what's inside. Actually, this function is quite simple

```float cylinder_volume(float height, float diameter)
{  float volume = M_PI * pow(diameter/2, 2) * height;
return volume;
}
```

Suppose, instead of milliliters, an answer in fluid ounces is needed, say for a recipe. Use the following functions, together with float cylinder_volume(float, float) to implement a US measurement version float cylinder_volume_oz(float, float) like this:

```float inch_to_mm(float inches)
/* PURPOSE:   Function to compute the volume of a cylinder
RECEIVES:  inches - value in inches to convert to millimeters
RETURNS:   the converted value
REMARKS:   1 inch = 25.4 millimeters
*/
{  const float MM_PER_INCH = 25.4;
return inches *  MM_PER_INCH;
}

float mm_cubed_to_oz(float mm3)
/* PURPOSE:   Function to convert cubic millimeters to U.S. ounces
RECEIVES:  mm3 - volume in cubic millimeters
RETURNS:   volume in ounces
REMARKS:   1 fluid U.S. ounce = 29.586 milliliters
*/
{  const float MM_CUBED_PER_OZ = 29.586;
return value_to_convert / MM_CUBED_PER_OZ;
}

float cylinder_volume(float height, float diameter)
/* PURPOSE:   Function to compute the volume of a cylinder
RECEIVES:  height - the height in millimeters
diameter - the diameter in millimeters
RETURNS:   volume - in cubic milliliters
*/
{  float volume = M_PI * diameter * diameter * height / 4;
return volume;
}

```
```int cylinder_volume_oz(float height, float diameter)
/* PURPOSE:   Function to compute the volume of a cylinder
RECEIVES:  height - the height in inches
diameter - the diameter in inches
RETURNS:   volume - in fl. oz.
*/
{```

```}

int main()
{  float height;
float diameter;
float volume;

cout << "Please enter the height (in inches)" << "\n";
cin >>  height;

cout << "Please enter the diameter (in inches)" << "\n";
cin >>  diameter;

volume = cylinder_volume_oz(height, diameter);
cout << "The volume is " << volume << "ounces" << "\n";

return EXIT_SUCCESS;
}
```

### P1. Writing Functions

Productivity Hint 5.1 in the text suggests several enhancements to the future value function. Here is the function from the text.

```float future_value(float initial_balance, float p, int nyear)
/* PURPOSE:  computes the value of an investment with compound interest
RECEIVES: initial_balance - the initial value of the investment
p-the interest rate as a percent
nyear-the number of years the investment is held
RETURNS:  the balance after nyears years
REMARKS:  Interest in compounded monthly
*/

{  float b = initial_balance * pow(1 + p / (12 * 100), 12 * nyear);
return b;
}

int main()
{   cout << "Please enter the initial investment: ";
float initial_balance;
cin >> initial_balance;
cout << "Please enter the interest rate in percent: ";
float rate;
cin >> rate;
cout << "Please enter the number of years: ";
float nyears;
cin >> nyears;
float balance = future_value(initial_balance, rate, nyears);
cout << "After " << nyears << ", the initial investment of " << initial_balance <<
" grows to " << balance << "\n";

return EXIT_SUCCESS;
}
```

Change the future_value function to compute the value of the investment when there are npayments regular interest payments per year (instead of 12 monthly payments). Supply a main function that calls your changed function.

What is the value of a \$2000 investment after 6 years at 12 percent if interest is compounded quarterly?

Change the future_value function to accept holding an investment for a fractional number of years, fyears. Supply a main function that calls your changed function.

What is the value of a \$2000 investment after 6.5 years at 12 percent if interest is compounded weekly (52 weeks/year)?

### R2. Function Names

The following code can be used to test a text mode function's interface. It's called a test harness, because any function can be used in it, simply by replacing foo() with the new function call and the desired parameters and returns.

```float foo(int n)
/* PURPOSE:  Test harness for calls to console programs
RETURNS:  Programmer specified return value
REMARKS:  Used to test parameter and return passing.
*/
{  cout << "Got to " << "foo" << " with parameter " << n << "\n";
return n * 0.1;               /* to simulate function activity */
}

int main()
{  int give = 3;                /* any test value can be entered here */
float get;
get = foo(give);
cout << "Returned from " << "foo" << " with " << get << "\n";

return EXIT_SUCCESS;
}

```

Why are the following functions badly named? Try using them in the test harness. What happened? What names would be better?

• float 2toTheN(int n)
/* compute a power of 2 */

• float(int max_value)
/* generate a random floating point number between 0 and max_value */

• float rt(float x)
/* computes the square root */

Sooner or later, you'll encounter cryptically named and uncommented work like the following.

```bool cn(Employee e1, Employee e2)
{  return e1.get_name() == e2.get_name();
}

bool cs(Employee e1, Employee f)
{  return e1.get_salary() == f.get_salary();
}

bool c4dup(Employee joe, Employee mary)
{  return  cn(joe, mary) and cs(joe, mary);
}

int main()
{  Employee john;
Employee jane;

if(c4dup(john, jane))
cout << "Same" << "\n";
else
cout << "Different" << "\n";

return EXIT_SUCCESS;
}
```

What do these functions do ?

Rewrite them with comments and more descriptive function and parameter names. Use:

```/* PURPOSE:
RETURNS:
*/
```

### P2. Return Values

In functions with more complicated branching of control, one way to insure a reasonable return value is to gather together all the possibilities and issue only one return statement from the very end of the block statement. Rewrite the points_of_compass function as follows:

1. Introduce an additional variable string direction_string
2. Be more clever about the logic--first compute the major direction (north, east, south, west), then append an east or west if necessary
3. Return the direction_string from the end of the function only
```string points_of_compass(int degrees)
/* PURPOSE:   Convert a numeric compass position to it's verbal equivalent
RECEIVES:  degrees - the compass needle angle in degrees
RETURNS:   the value as a compass direction ("N", "NE", ...)
*/
{  degrees = degrees % 360;
float octant = degrees / 45.0 - 0.5;

if (octant >= 7)
return "North West";
else if (octant >= 6)
return "West";
else if (octant >= 5)
return "South West";
else if (octant >= 4)
return "South";
else if (octant >= 3)
return "South East";
else if (octant >= 2)
return "East";
else if (octant >= 1)
return "North East";
else if (octant >= 0)
return "North";
else
return "North West";
}

int main()
{  int degrees;

cin >> degrees;

string direction = points_of_compass(degrees);
cout << "You are heading " << direction << "\n";
return EXIT_SUCCESS;
}

```

### R4. Parameters

Consider the following functions:

```   string lowercase(string s);
void swap (float& a, float& b);
float can_volume(float h, float r);
```

and these variables:

```   string greeting = Hello!"
int a;
int b;
float x;
```

What is wrong with each of the following function calls ?

• greeting = lowercase(a);

• float_swap(a, b)

• greeting = can_volume(a,b)

### Side Effects, Procedures and Reference Parameters

Formally, procedures differ from functions in that they do not, ideally, have a return value, or if they do, it's production is not the principal purpose of the procedure. The lack of a return value might result from any of several conditions:

• nothing needs to be returned
```
void print_result(string out_string)
/* PURPOSE:   print a string to the screen
REMARKS:   Procedure doesn't need to return confirmation of successful print
*/
{   cout << "The result is " << out_string << endl;
}
```
• more than one thing is changed, but return could only handle one of them:
```
void reset(float& level, Time& starttime, string& name, int& index)
/* PURPOSE:   Reset variables to default values
RECEIVES:  level - the level to be reset
starttime - the time to be reset
name - the name to be reset
index - the index to be reset
REMARKS:   All parameters are passed by reference to enable changing them in place
*/
{  level = 0.0;
starttime = Time();
name = "";
index++;
}
```
• the object to be changed is in the global scope
```
/* PURPOSE:   Set an employee's salary
USES:      Global top_salary
REMARKS:   top_salary keeps the maximum salary of all seen by set_salary
*/
void set_salary(Employee employee, float new_salary)
{  if (new_salary > top_salary)
top_salary = new_salary;
employee.set_salary(new_salary);
}
```

#### R5. Tracing Reference Parameters

The street gambler's game of 3-Card Monte is deceptively simple. Three cards are placed face down on a table. One of the three is the Queen of Spades, and you are shown where it is. The dealer then re-arranges the cards, and asks "Where is the Queen?"

```void swap(string& a, string& b)
{  string temp;
temp = a;
a = b;
b = temp;
}

int three_card_monte(string card1, string card2, string card3)
{  swap(card1, card2);
swap(card2, card3);
swap(card1, card3);

if (card1 == "queen")
return 1;
else if (card2 == "queen")
return 2;
else /* (card3 == "queen") */
return 3;
}

int main()
{  string first = "queen";
string second = "king";
string third = "ace";
int guess;
int location;

location = three_card_monte(first, second, third);

cout << "Where's the Queen ( 1, 2 or 3 ) ? ";
cin >> guess;

if (guess == location)
cout << "Congratulations!" << "\n";
else
cout << "Better luck next time, it was number " << location << "\n";

return EXIT_SUCCESS;
}

```

What are the values of the parameters to three_card_monte(card1, card2, card3)

To what variable do a and b refer in the first, second and third calls to swap(a,b)

 call a b 1 2 3

#### P3. Programming with Reference Parameters

Write a function sort3d() that sorts three integers in decreasing order. You may use:

```void sort2d(int& a, int&b)
{   int temp;

if (a < b)
{  temp = b;
a = b;
b = temp;
}
}
```

### R6. Variable scoping

Generally, we want to encourage you to define a variable when you first need it, but you have to pay attention to the scope. Find what's wrong with this function's variable scoping, then fix it.

```/* PURPOSE:   Select the maximum of three integer values
*/

int maximum(int i, int j, int k)
{  if (i > j)
{  int a;
a = i;
}
else
{   a = j;
}

if (k > a)
{   return k;
}
else
{   return a;
}
}

```

#### P4. Eliminating Global Variables

Global variables may "work", but the advantages they offer are outweighed by the confusion they can cause. Since all functions can set a global variable, it is often difficult to find the guilty party if the global variable is set to the wrong value.

```int maximum;

void set_max(int a)
/* PURPOSE:   Updates maximum if parameter is larger
RECEIVES:  a - the value to compare against maximum
REMARKS:   Uses global int maximum
*/
{  if (maximum < a)
{   maximum = a;
}
}
void max3(int i, int j, int k)
{  maximum = i;
set_max(j);
set_max(k);
return maximum;
}

int main()
{   cout << "Please enter the first integer: ";
cin >> i;
cout << "Please enter the second integer: ";
cin >> j;
cout << "Please enter the third integer: ";
cin >> k;
maximum = max3(i, j, k);

cout << "The maximum is " << maximum << "\n"
return EXIT_SUCCESS;
}
```

Re-write max3() to avoid the use of global variables, and to preserve the logic of the function.

### P5. Stepwise refinement

A call-tree diagram can help organize a large number of related functions. Consider drawing a house like this one using the code library's graphics functions.

It can be done by organizing calls like these:

```draw_house()
draw_window()
draw_front()
draw_roof()
draw_door()
```

into a call tree like this

```draw_house
|
+---------- draw_front
|               |
|               +----------- draw_window (3 times)
|               |
|               +----------- draw_door
|
+---------- draw_roof

```

Write a program that implements these functions to draw a house. (Hint: draw_window needs a parameter to specify the location of the window.)

### R7. Walkthroughs

Check the arguments accepted by three_card_monte(). What happens if one of them is empty, e.g. three_card_monte("queen","king","") ?

What changes to the function would you recommend ?

### P6. Recursion

Consider a function int digits(int) which finds the number of digits needed to represent an integer. For example, digits(125) is 3 because 125 has three digits (1, 2, and 5). The algorithm is defined as:

if n < 10, then digits(n) equals 1. Else, digits(n) equals digits(n / 10) + 1.

(Why? If n is less than 10, one digit is required. Otherwise, n requires one more digit than n/10.)

For example, if called as int num_digits = digits(1457), the following trace results:

```digits(1457)
= digits(145) + 1
= digits(14) + 1 + 1
= digits(1) + 1 + 1 + 1
= 1 + 1 + 1 + 1
```

Do a trace of digits(32767)

Write int digits(int) to be called by the following main():

```int main()
{  int test_value;

cout << "Please enter a number " << "\n";
cin >>  test_value;

int ndigits = digits(test_value);
cout << "You need " << ndigits << " bits to represent " << test_value << " in decimal\n";
return EXIT_SUCCESS;
}
```