• No results found

SELF-TEST EXERCISES 2.1

2.5 The design and testing of programs

In the subsequent chapters of this book we shall meet the full range of Fortran 90 statements and facilities, and will begin to appreciate the richness of the language which is the basis of its ability to enable the Fortran programmer to solve an enormously wide range of problems easily and efficiently. The exercises and examples that will be used to illustrate that richness will, however, necessarily be brief, so that their complexity will not get in the way of the points that they are trying to make. Real programs will almost always be substantially larger than those that you will find in this book.

Although all the worked examples will develop the program's design by means of structure plans, it may sometimes seem that this is making the process unnecessarily difficult. Such an attitude could not be more wrong!

Computer programming is an activity that is extremely interesting, and one which can often exert a very considerable fascination upon those involved. It

The design and testing of programs 29 is so interesting because it requires a careful blend of knowledge from several

different areas, and because the programmer is involved in a creative process which has almost no constraints, beyond the necessity to be logically consistent and obey the syntax and semantics of the language being used. In this, it has similarities with both pure mathematics and with the fine arts. As a consequence of this freedom, the programmer is free to be very creative, but abuse of this freedom will inevitably lead to poor programs.

In mathematics, a correct but ugly proof makes mathematicians uneasy, and they will strive to find an elegant proof to replace it. The same is true for computer programs, and a correct but ugly program will make people want to redesign and rewrite it. It is important to emphasize that this is not just aesthetics coming into play, for there are sound, very practical, reasons for writing elegant programs.

• The first of these is that an ugly program is almost synonymous with poor design, and with coding that was begun before the program was well thought out. This results in programs that can only be made to work correctly with considerable difficulty. Indeed, if a program is badly enough constructed, even after it has solved several test cases correctly the writer may have an uneasy feeling that it is not really reliable.

The experience of many people over many years has shown that the time spent in careful initial design, before writing any code, is more than regained during the process of verification (often called debugging).

The larger the project the more this principle comes into play. However, this does not mean that small projects do not benefit from some initial planning. Careful initial design always pays off, even on the smallest project.

• A second issue, and to many people of even more importance than the initial development of a program, is that of maintainability of programs.

Programs need maintenance for several reasons. If a programming project goes beyond a certain small size, it will almost certainly, at some point during its lifetime, be found to have errors in special circumstances that were not thought of, or were incorrectly handled, during the initial design. Programs often have an unexpectedly long life (sometimes to the embarrassment of their authors!). Almost inevitably, most programs will, therefore, be subsequently extended to deal with new problems not in the original requirements. As a result, it is quite normal for more time and effort to be spent in extending and maintaining a program than was spent in originally developing it. The phrase 'write once and read many times' is a truism in programming.

• Finally, if you have written a program of more than parochial interest, you will undoubtedly receive requests from friends and colleagues for copies. The world is full of different types of computers. Imagine your colleagues' distress if they cannot readily compile and execute your

30 First steps in Fortran 90 programming

program on their machines! You may also find that the computer on which a program was originally developed is being replaced with a new one - a circumstance that seems to be happening with ever-increasing frequency. Therefore, writing portable programs is important. Only for well-considered reasons should non-standard or obsolescent features of Fortran be used.

In summary, programs should be well designed before they are begun.

This will lead to reliable, efficient, easily maintained, portable programs that are enjoyable to create and to work with.

There are many elements that go into good program design, and many approaches that have been developed to assist programmers to develop well-designed programs. However, regardless of the detailed approach that is used, there are a number of underlying principles that must always be incorporated in the design of any program, of which the following are the most important:

• Completely understand what the program is supposed to accomplish.

What are the inputs and outputs supposed to be? This sounds too trivial to be worth mentioning, but it is not. It is all too easy not to have all the facts clearly understood before starting programming. This will lead to much painful and expensive redesign at a later stage.

Make the input and output clear to understand for the program user. Make the input form as easy as possible and the output form as clear and useful as possible.

• Have a clear design for the method to be used to solve the problem. Write it down. We have already introduced one way to do this and will expand on this in later chapters. However, there are other approaches, and you should choose one that you feel comfortable with. It is surprising how often this stage has to be reworked until a correct solution is found.

• Look to see what functionality you can find in existing procedure libraries.

We shall have more to say about this in Chapter 4, but we have already seen in Example 2.1 how a subroutine can be used to avoid the need to write all the program oneself. Reinventing the wheel is not an economical use of your time!

• When writing the program use a modular design. We shall discuss this topic in some detail in Chapter 4 and so will not say more here, except to point out that a good rule of thumb is that no single block of code (or procedure, see Chapter 4) should be longer than about 50 lines, excluding any comments.

• Use descriptive names for variables and program units and be lavish with comments. Code with few or no comments is usually impossible for even

• the author to understand once a few weeks have passed since its creation.

The design and testing of programs 31

• Perform as much error checking on the input as is possible. Moreover, perform checks on the success of the internal stages of a calculation.

Include, as part of the output, any problems the program detects and how the accuracy of the answer is being affected. This is actually a somewhat complicated topic and only a few introductory comments can be made here.

For input error checking, try to catch, and report clearly back to the user, every error that the input data might contain. For example, if one of the data items is the number of items to be processed, this number should be checked to see that it is not negative, and should also be checked to see that it is not so large that the capacity of the program will be exceeded. This is an obvious type of check to make. A less obvious class of checks is on the self-consistency of the data. For example, if a program is supposed to take three points as input and calculate the radius and centre of the circle passing through them, the points should be checked to see that they do not lie on a straight line. If they are exactly collinear the radius is infinite and the coordinates of the centre have become indeterminate. Another check is to test that all the points are distinct. If they are not, then there are an infinite number of solutions.

For errors that can be detected while the program is executing, consider checks on how many iterations are being performed in trying to converge to a solution. If this becomes too large, the user should be informed and given an option to terminate the process.

Returning to the problem of determining the circle through three points, suppose the points are almost collinear or almost coincident. This is more difficult to detect than exact collinearity or coincidence. What does 'almost' mean here? How can a precise numerical value be given for 'almost'? There is a solution. The centre of the circle can be determined as the result of solving a pair of simultaneous linear equations. There are well established mathematical techniques, which are, however, outside the scope of this book, for estimating the condition number of such linear systems of equations. This analysis will detect near linearity or coincidence of points and can be used to report back to the user how many digits, if any, of the answer are accurate.

Finally, test the program by using cases that execute every part of the program, including your input error tests and calculation problem tests.

Although it may sound obvious, ensure that you know what the correct answer should be for those tests which are designed to run to completion.

Just because your program produces an answer doesn't mean that it is the correct one!

These techniques do not take the interest and challenge out of programming, making it a mechanical process. Instead, they make a program easier to develop and maintain, thereby making the process more interesting and

32 First steps in Fortran 90 programming

less painful. All of us have, at some time in our lives, been faced with the problem of trying to find the error in a badly written and badly documented program (written by someone else, of course!) and we do not recommend it to anyone.

We shall return to the question of testing programs in the Intermission between Parts I and II of this book, but we cannot overemphasize what a vitally important part of the programming process it is. Even with apparently simple programs, such as those which you will write in response to the exercises in the first part of this book, you should always thoroughly test them to ensure that they produce the correct answers from valid data, and react in a predictable and useful manner when presented with invalid data.