BIG C++ Cay Horstmann & Timothy Budd
Laboratory Notebook Chapter 5 - Functions
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.
To gain experience in
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 */ double cylinder_volume(double height, double diameter) { . . . }
To use this function to measure the volume of a can of Blatz Lite, just supply parameters and get its return.
double v = cylinder_volume(2 * can_depth, 1.5 * can_diameter);
Notice that you can use the function without knowing the implementation. Actually, this function is quite simple.
double cylinder_volume(double height, double diameter) { double volume = PI * pow(diameter/2, 2) * height; return volume; }
Suppose that instead of milliliters an answer in fluid ounces is needed, say for a recipe. Use the following functions, together with double cylinder_volume(double, double) to implement a US measurement version double cylinder_volume_oz(double, double) like this:
double inch_to_mm(double 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 double MM_PER_INCH = 25.4; return inches * MM_PER_INCH; } double mm_cubed_to_oz(double 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 double MM_CUBED_PER_OZ = 29.586; return value_to_convert / MM_CUBED_PER_OZ; } double cylinder_volume(double height, double 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 */ { double volume = PI * diameter * diameter * height / 4; return volume; }
int cylinder_volume_oz(double height, double 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() { double height; double diameter; double 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 0; }
Productivity Hint 5.1 in the text suggests several enhancements to the future value function. Here is the function from the text.
double future_value(double initial_balance, double 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 */ { double b = initial_balance * pow(1 + p / (12 * 100), 12 * nyear); return b; } int main() { cout << "Please enter the initial investment: "; double initial_balance; cin >> initial_balance; cout << "Please enter the interest rate in percent: "; double rate; cin >> rate; cout << "Please enter the number of years: "; double nyears; cin >> nyears; double balance = future_value(initial_balance, rate, nyears); cout << "After " << nyears << ", the initial investment of " << initial_balance << " grows to " << balance << "\n"; return 0; }
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.
/* paste program here */
What is the value of a $5100 investment after 4 years at 11 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 $5100 investment after 5.5 years at 9 percent if interest is compounded weekly (52 weeks/year)?
Write a function that calculates the area of a rectangle. Then, write a program that asks the user for the width and height of the rectangle and displays the area. The statement to print the area might look like the following:
cout << "Area: "<<rectangle_area (width, height) << endl;
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.
double foo(int n) /* PURPOSE: Test harness for calls to console programs RECEIVES: Programmer specified parameter 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 */ double get; get = foo(give); cout << "Returned from " << "foo" << " with " << get << "\n"; return 0; }
Why are the following functions badly named? Try using them in the test harness. What happened? What names would be better?
Sooner or later, you will 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_city() == f.get_city() and e1.get_state() = f.get_state()); } bool c4dup(Employee joe, Employee mary) { return cn(hank, mable) and cs(hank, mable); } int main() { Employee jerry; Employee jenny; if(c4dup(jerry, jenny)) cout << "Same" << "\n"; else cout << "Different" << "\n"; return 0; }
What do these functions do ?
Rewrite them with comments and more descriptive function and parameter names. Use:
/* PURPOSE: RECEIVES: RETURNS: */
/*paste your code here*/
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:
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; double 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; cout << "Please enter the compass heading (in degrees): "; cin >> degrees; string direction = points_of_compass(degrees); cout << "You are heading " << direction << "\n"; return 0; }
Consider the following functions:
string propercase(string z); void swapit(int& a, double& b); double cylinder_height(int h, double r);
and these variables:
string greeting = Hello!" int f; double g; double x;
What is wrong with each of the following function calls ?
Procedures differ from functions since they generally do not have a return value. Sometimes a procedure may have a return value but the generation of a return value is not the main purpose of the procedure. Several conditions may cause a function to not return a value:
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; }
void reset(double& 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++; }
/* 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, double new_salary) { if (new_salary > top_salary) top_salary = new_salary; employee.set_salary(new_salary); }
The street gambler's game of 3-Card Monte is deceptively simple. Three cards are placed face down on a table, one of which is the Queen of Spades. You are shown which it is and the dealer then rearranges 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 0; }
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)
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 = a; a = b; b = temp; } }
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 is wrong with this function's variable scoping, then fix it.
/* PURPOSE: Select the maximum of three integer values RECEIVES: ints i, j, k */ 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; } }
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; } } int max3(int i, int j, int k) { maximum = i; set_max(j); set_max(k); return maximum; } int main() { int i, j, k; 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 0; }
Re-write max3() to avoid the use of global variables, and to preserve the logic of the function.
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.)
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 ?
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 0; }
The following program computes the percentage difference between last year's raise and this year's raise.
#include <iostream> using namespace std; double compute_change(double ns, double ls, double os) { return (((ns-ls)/(ls-os))*100); } int main() { double new_sal, old_sal, last_sal; cout << "New salary: "; cin >>new_sal; cout << "\nLast year's salary: "; cin >>last_sal; cout <<"\nPrevious year's salary: "; cin >> old_sal; cout <<compute_change(new_sal,last_sal,old_sal); return 0; }
Rewrite the compute_change function using two different methods to insure that the denominator (last year's salary - previous year's salary) is not equal to zero./*insert your code here*/
/*insert your code here*/
Do not forget to send your answers when you are finished.