next up previous contents
Next: Procedures Up: Professional Programmer's Guide to Previous: Character Handling and Logic

Control Statements

Executable statements are normally executed in sequence except as specified by control statements. The END= and ERR= keywords of input/output statements can also affect the execution sequence.

Control Structures

Branches

The best way to select alternative paths through a program is to use the block-IF structure: this may comprise a single block to be executed when a specified condition is true or several blocks to cover several eventualities. Where the IF-block would only contain one statement it is possible to use an abbreviated form called (for historical reasons) the logical-IF statement.

There is also a computed GO TO statement which can produce a multi-way branch similar to the ``case" statements of other languages.

Loops

Another fundamental requirement is that of repetition. If the number of cycles is known in advance then the DO statement should be used. This also controls a block of statements known as the DO-loop. A CONTINUE statement usually marks the end of a DO-loop.

Fortran has no direct equivalent of the ``do while" and ``repeat until" forms available in some program languages for loops of an indefinite number of iterations, but they can be constructed using simple GO TO and IF statements.

Other Control Statements

The STOP statement can be used to terminate execution. Other statements which affect execution sequence are described in other sections: the END statement was covered in section 4.7; procedure calls including the CALL and RETURN statements are described in section 9.

IF-Blocks

The simplest form of IF-block looks like this:

 
      IF(N .NE. 0) THEN 
         AVERAG = SUM / N 
         AVGSQ  = SUMSQ / N 
      END IF

The statements in the block are only executed if the condition is true. In this example the statements in the block are not executed if N is zero in order to avoid division by zero.

The IF-block can also contain an ELSE statement to handle the alternative:

 
      IF(B**2 .GE. 4.0 * A * C) THEN 
          WRITE(UNIT=*,FMT=*)'Real roots' 
      ELSE 
          WRITE(UNIT=*,FMT=*)'No real roots' 
      END IF

Since the IF statement contains a logical expression its value can only be true or false, thus one or other of these blocks will always be executed.

If there are several alternative conditions to be tested, they can be specified with ELSE IF statements:

 
      IF(OPTION .EQ. 'PRINT') THEN 
           CALL OUTPUT(ARRAY) 
      ELSE IF(OPTION .EQ. 'READ') THEN 
           CALL INPUT(ARRAY) 
      ELSE IF(OPTION .EQ. 'QUIT') THEN 
           CLOSE(UNIT=OUT) 
           STOP 'end of program' 
      ELSE 
           WRITE(UNIT=*,FMT=*)'Incorrect reply, try again...' 
      END IF

There can be any number of ELSE IF blocks but in each case one, and only one, will be executed each time. Without an ELSE block on the end an nothing would have happened when an invalid option was selected.

Block-IF General Rules

The general form of the block-if structure is as follows:

 
      IF( logical-expression ) THEN 
            a block of statements  
      ELSE IF( logical-expression ) THEN 
            another block of statements  
      ELSE 
            a final block of statements 
      END IF
The IF THEN, ELSE IF, and ELSE statements each govern one block of statements. There can be any number of ELSE IF statements. The ELSE statement (together with its block) is also optional, and there can be at most one of these.

The first block of statements is executed only if the first expression is true. Each block after an ELSE IF is executed only if none of the preceding blocks have been executed and the attached ELSE IF expression is true. If there is an ELSE block it is executed only if none of the preceding blocks has been executed.

After a block has been executed control is transferred to the statement following the END IF statement at the end of the structure (unless the block ends with some statement which transfers control elsewhere).

Any block can contain a complete block-IF structure properly nested within it, or a complete DO-loop, or any other executable statements (except END).

It is illegal to transfer control into any block from outside it, but there is no restriction on transferring control out of a block.

The rules for logical expressions are covered in section 7.7.

Guidelines

The indentation scheme shown in the examples above is not mandatory but the practice of indenting each block by a few characters relative to the rest of the program is strongly recommended. It makes the structure of the block immediately apparent and reduces the risk of failing to match each IF with an END IF. An indenting scheme is especially useful when IF-blocks are nested within others. For example:

 
      IF(POWER .GT. LIMIT) THEN 
          IF(.NOT. WARNED) THEN 
              CALL SET('WARNING') 
              WARNED = .TRUE. 
          ELSE 
              CALL SET('ALARM') 
          END IF 
      END IF
The limited width of the statement field can be a problem when IF-blocks are nested to a very great depth: but this tends to mean that the program unit is getting too complicated and that it will usually be beneficial to divide it into subroutines. If you accidentally omit an END IF statement the compiler will flag the error but will not know where you forgot to put it. In such cases the compiler may get confused and generate a large number of other error messages.

When an IF-block which is executed frequently contains a large number of ELSE IF statements it will be slightly more efficient to put the most-likely conditions near the top of the list as when they occur the tests lower down in the list will not need to be executed.

DO-Loops

The DO statement controls a block of statements which are executed repeatedly, once for each value of a variable called the loop-control variable. The number of iterations depends on the parameters of the DO statement at the heads of the loop. The first item after the keyword ``DO" is the label which is attached to the last statement of the loop. For example:

 
*Sum the squares of the first N elements of the array X 
      SUM = 0.0 
      DO 15, I = 1,N 
          SUM = SUM + X(I)**2 
15    CONTINUE
If we had wanted only to sum alternate elements of the array we could have used a statement like:
DO 15,I = 1,N,2
and then the value of I in successive loops would have been 1, 3, 5, etc. The final value would be N if N were odd, or only to N-1 if N were even. If the third parameter is omitted the step-size is one; if it is negative then the steps go downwards. For example
 
      DO 100,I = 5,1,-1 
          WRITE(UNIT=*,FMT=*) I**2 
100   CONTINUE
will produce 5 records containing the values 25, 16, 9, 4, and 1 respectively.

Loops can be nested to any reasonable depth. Thus the following statements will set the two dimensional array FIELD to zero.

 
      REAL FIELD(NX, NY) 
      DO 50, IY = 1,NY 
         DO 40, IX = 1.NX 
             FIELD(IX,IY) = 0.0 
40       CONTINUE 
50    CONTINUE

General Form of DO Statement

The DO statement has two forms:
DO label , variable = start , limit, step
DO label , variable = start , limit
In the second form the step size is implicitly one.

The label marks the final statement of the loop. It must be attached to an executable statement further on in the program unit. The rules permit this statement to be any executable statement except another control statement, but it strongly recommended that you use the CONTINUE statement here. CONTINUE has no other function except to act as a dummy place-marker.

The comma after the label is optional but, as noted in section 1.4, is a useful precaution.

The variable which follows is known as the loop control variable or loop index; it must be a variable (not an array element) but may have integer, real, or double precision type.

The start, limit, and step values may be expressions of any form of integer, real, or double precision type. If the step value is present it must not be zero, of omitted it is taken as one. The number of iterations is computed before the start of the first one, using the formula:

Note that if the limit value is less than start the iteration count is zero unless step is negative. A zero iteration count is permitted but means that the contents of the loop will not be executed at all and control is transferred to the first statement after the end of the loop. The loop control variable does not necessarily reach the limiting value, especially if the step-size is larger than one.

Statements within the loop are permitted to alter the value of the expressions used for start, limit, or step but this has no effect on the iteration count which is fixed before the first iteration starts.

The loop control variable may be used in expressions but a new value must not be assigned to it within the loop.

DO-loops may contain other DO-loops completely nested within them provided that a different loop control variable is used in each one. Although it is permissible for two different loops to terminate on the same statement, this can be very confusing. It is much better to use a separate CONTINUE statement at the end of each loop. Similarly complete IF-blocks may be nested within DO-loops, and vice-versa.

Other control statements may be used to transfer control out of the range of a DO-loop but it is illegal to try to jump into a loop from outside it. If you exit from a loop prematurely in this way the loop control variable keeps its current value and may be used outside to determine how many loops were actually executed.

After the normal termination of a DO-loop the loop control variable has the value it had on the last iteration plus one extra increment of the step value. Thus with:

 
       DO 1000, NUMBER = 1,100,3 
1000   CONTINUE
On the last iteration NUMBER would be 99, and on exit from the loop NUMBER would be 102. This provision can be useful in the event of exit from a loop because of some error:
 
       PARAMETER (MAXVAL = 100) 
       REAL X(MAXVAL) 
       DO 15, I = 1,MAXVAL 
            READ(UNIT=*, FMT=*, END=90) X(I) 
15     CONTINUE 
90     NVALS = I - 1
The action of the statement labelled 90 is to set NVALS to the number of values actually read from the file whether there was a premature exit because the end-of-file was detected or it reached the end of the array space at MAXVAL.

Guidelines

If you a loop-control variable of any type other than integer there is a risk that rounding errors will accumulate as it is incremented repeatedly. In addition, if the expressions for the start, limit, and step values are not of integer type the number of iterations may not be what you expect because the formula uses the INT function (not NINT). None of these problems can occur if integer quantities are used throughout the DO statement.

Logical-IF Statement

The logical-IF statement is best regarded as a special case of the IF-block when it only contains one statement. Thus:

 
       IF(E .NE. 0.0) THEN 
           RECIPE = 1.0 / E 
       END IF
can be replaced by a single logical-IF statement:
IF(E .NE. 0.0) RECIPE = 1.0 / E

The general form of the logical-IF statement is:
IF( logical-expression ) statement
The statement is executed only if the logical expression has a true value. Any executable statement can follow except DO, IF, ELSE IF, ELSE, END IF, or END.

Unconditional GO TO Statement

The unconditional GO TO statement simply produces a transfer of control to a labelled executable statement elsewhere in the program unit. Its general form is:
GO TO label
Note that control must not be transferred into an IF-block or a DO-loop from outside it.

Guidelines

The unconditional GO TO statement makes it possible to construct programs with a very undisciplined structure; such programs are usually hard to understand and to maintain. Good programmers use GO TO statements and labels very sparingly. Unfortunately it is not always possible to avoid them entirely in Fortran because of a lack of alternative control structures.

The next example finds the highest common factor of two integers M and N using a Euclid's algorithm. It can be expressed roughly: while (M N) subtract the smaller of M and N from the other repeat until they are equal.

 
       PROGRAM EUCLID 
       WRITE(UNIT=*, FMT=*) 'Enter two integers' 
       READ(UNIT=*, FMT=*) M, N 
10       IF(M .NE. N) THEN 
            IF(M .GT. N) THEN 
                M = M - N 
            ELSE 
                N = N - M 
            END IF 
            GO TO 10 
       END IF 
       WRITE(UNIT=*, FMT=*)'Highest common factor = ', M 
       END

Computed GO TO Statement

The computed GO TO statement is an alternative to the block-IF when a large number of options are required and they can be selected by the value of an integer expression. The general form of the statement is:
GO TO( label1, label2, ... labelN ), integer-expression
The comma after the right parenthesis is optional.

The expression is evaluated; if its value is one then control is transferred to the statement attached to the first label in the list; if it is two control goes to the second label, and so on. If the value of the expression is less than one or higher than N (where there are N labels in the list) then the statement has no effect and execution continues with the next statement in sequence. The same label may be present more than once in the list.

The computed GO TO suffers from many of the same drawbacks as the unconditional GO TO, since if its branches are used without restraint they can become impenetrable thickets. The best way is to follow the computed GO TO statement with the sections of code in order, all except the last terminated with its own unconditional GO TO to transfer control to the end of the whole structure.

Any computed GO TO structure could be replaced by an IF-block with a suitable number of ELSE IF clauses. If there are a very large number of cases then this would be a little less efficient; this has to be balanced against the increased clarity of the IF structure compared to the label-ridden GO TO.

An example of the use of the computed GO TO is given here in a subroutine which computes the number of days in a month, given the month number MONTH between 1 and 12, and the four-digit year number in YEAR. Note that each section of code except the last is terminated with a GO TO statement to escape from the structure.

 
      SUBROUTINE CALEND(YEAR, MONTH, DAYS) 
      INTEGER YEAR, MONTH, DAYS 
      GO TO(310,280,310,300,310,300,310,310,300,310,300,310)MONTH 
*           Jan Feb Mar Apr May Jun Jly Aug Sep Oct Nov Dec 
      STOP 'Impossible month number' 
*February: has 29 days in leap year, 28 otherwise. 
280   IF(MOD(YEAR,400) .EQ. 0  .OR. (MOD(YEAR,100) .NE. 0 
     $                          .AND. MOD(YEAR,4) .EQ. 0)) THEN 
          DAYS = 29 
      ELSE 
          DAYS = 28 
      END IF 
      GO TO 1000 
*   Short months 
300   DAYS = 30 
      GO TO 1000 
*   Long months 
310   DAYS = 31 
* return the value of DAYS 
1000  END

STOP Statement

The STOP statement simply terminates the execution of the program and returns control to the operating system. Its general form is:
STOP ' character constant '
The character constant (which must be a literal and not named constant) is optional: if present its value is ``made available" to the user; usually it the message appears on your terminal. For compatibility with Fortran66 it is possible to use a string of one to five decimal digits instead of the character constant.

Ideally a program should only return control to the operating system from one point, the end of the main program, where the END statement does all that is necessary. In practice, even in the best-planned programs, situations can arise which make it pointless to continue. If these are detected in the main program there is always the option of jumping to the END statement, but within procedures there may be no choice but to use a STOP statement.


next up previous contents
Next: Procedures Up: Professional Programmer's Guide to Previous: Character Handling and Logic

Clive Page
Tue Feb 27 11:14:41 GMT 2001