Home | C++ Tutorial | 5. Control Methods |     Share This Page
The skill that makes your computer think

More Control Examples

Here are some of the ways a program is controlled: Control by Event

An "event" is usually taken to mean a happening in the real world, something entirely outside the program. It could be a keypress, a mouse movement, or some other activity. Many examples of event control rely on system-specific extensions to the C++ language, but, since this is meant to be a generic introduction, I'll use an example that will probably run on any C++ compiler.

#include <iostream>
#include <ctime>
#include <string>

using namespace std;

void eraseOldTime(int size)
{
	while(size-- > 0) {
		// "\b" is the backspace character
		cout << "\b";
	}
}

string formatTime(time_t t)
{
	unsigned int p;
	string st = (string) "The time is now " + ctime(&t);
	// remove linefeed from ctime() output
	if((p = st.find('\n')) != string::npos) {
		st = st.substr(0,p);
	}
	return st;
}

void showTime(double delaySeconds)
{
	time_t startTime = time(NULL);
	time_t oldTime = startTime,newTime;
	string timeDisp;
	do {
		newTime = time(NULL);
		if(newTime != oldTime) {
			// erase previous time display
			eraseOldTime(timeDisp.size());
			timeDisp = formatTime(newTime);
			// "\a" is the bell character
			cout << timeDisp << "\a" << flush;
		}
		oldTime = newTime;
	}
	while(difftime(newTime,startTime) < delaySeconds);
	cout << "\nTime delay exceeded.\n";
}

int main()
{
	showTime(10);
	return 0;
}
      
Discussion

Q: Why use difftime() to find time differences? Everybody knows time_t variables are expressed in seconds.
Actually, this depends on the compiler, not some strict rule — time_t variables can contain anything at all, and if you want your program to be portable, avoid unnecessary assumptions. difftime() returns the number of seconds between two times provided as time_t variables.

Q: Why use "\b" for backspace? That seems unecessarily complicated. Why not use (char)8?
Again, for portability, don't make system-specific assumptions. An abstract identifier like "\b" is much more portable than assuming that a desired control character will aways have a particular number.

Q: It seems a shame to have to use those old-fashioned C-language function calls to acquire time values and manipulate them. Isn't there a new, up-to-date time class in C++?
Unfortunately, no. This remains an unsolved problem. The developers of C++ quickly realized that time is a cybernetic can of worms, and decided not to try to sort it all out in a "universal" way.

Control by Outcome

In this example, a decision to terminate a procedure is made based on a result's having an acceptable accuracy. The following square root computation method goes back well before the time of Sir Isaac Newton, who is normally given credit for it.

#include <iostream>
#include <iomanip>

using namespace std;

double square_root(double x)
{
	// "delta" is the acceptable error bound
	double delta = 1e-15;
	double y = (1+x)*.5,oldy;
	do {
		oldy = y;
		y = (x/y+y)*.5;
	}
	while(fabs(y-oldy) > delta);
	return y;
}

int main()
{
	for(double x = 1;x <= 20;x += 1) {
		double y = square_root(x);
		cout << setprecision(16);
		cout << "The square root of "
		<< setw(2) << x
		<< " is " << y
		<< endl;
	}
	return 0;
}
      
Discussion

Experiment with this program. Try changing the "delta" value to 1e-30. What happens? Why? Try changing it to 1e-3. What happens? Why?

(Remember that a double variable has limited precision. It can never resolve a root near 1 to an error bound of 1e-30.)

Try producing an new variable z that is the square of y (double z = y * y) and printing it. Does z equal the original variable x? Why not?

Q: I see you are using a double variable as a loop index. I thought this was a bad idea.
If a double is assigned only integer values, it can be relied on to behave itself in most cases.
Q: What is "setprecision(16)?
It instructs "cout" to print FP numbers with 16 decimal places. Otherwise "cout" might round off the result arbitrarily. Remember this trick when you must have the maximum possible numerical display accuracy. And also realize the number 16 is quite arbitrary, and may change with time — future FP variables may be capable of more precision.

Q: Isn't there a square root function defined in the header <cmath>? Why not just use that instead?
Because someday you may have to write a function that doesn't already exist, or show that you know how to write a unique new function. Chances are, if you are successful as a programmer, it won't result entirely from using functions written by others.
Control by Design

Sometimes the program's desired outcome is known at the time the program is designed. In this case, much simpler design choices can be made. Like this multiplication table generator:
#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
	int max = 12;
	for(int row = 1;row <= max;row++) {
		for(int col = 1;col <= max;col++) {
			cout << setw(5) << row * col;
		}
		cout << endl;		
	}
	return 0;
}
      
Discussion

Notice about this program that it has one for-loop inside another. Examine the program, line by line, until you understand how it works. And experiment with the code — try changing things to see what happens. For example, change "max" from 12 to 20. What happens? Why?

Q: I removed the "<< setw(5)" statement. The output got all messed up. Why?

The call "setw(n)" makes "cout" use a fixed field width of n characters. This "beautifies" the display by giving all the colums the same width.


These pages are Copyright © 2000, P. Lutus. All rights reserved.

www.arachnoid.com Main Page
Home | C++ Tutorial | 5. Control Methods |     Share This Page