All our programs so far have required the storage of only a few values, and could therefore be written using only a few variables. For example, the average mark program of Figure 8 on page 18 required only variables for a mark, the total mark, the count of the marks and the average. When large numbers of values have to be stored, it becomes impracticaI or impossible to use different variables for them all. If the average mark program were rewritten to compute average marks for five subjects, we should require five variables, say MARK1 ... MARK5 for the marks, five variables for the totals, and five for the averages. This could be done, but the program would be rather repetitive. The situation is even worse if, after computing the averages, the program is required to print a list showing, for each student and subject, the student's mark and the difference between the mark and the average. This could conceivably be done if the number of students were given in advance, but the program would be extremely cumbersome. If, as in the example, the number of students is not given but determined by counting, the task is impossible, as there is no way of knowing how many variables will be required.
We need to store all the marks in order in a list or other structure to which we can apply a name, and refer to individual marks by a combination of the name and a number or numbers indicating the position of a mark in the list or structure.
In mathematics, an ordered list of items is called a vector of dimension . If the vector is denoted by , the items, usually called the components or elements of the vector, are denoted by .
FORTRAN uses a structure similar to a vector called an array. An array A of dimension N is an ordered list of N variables of a given type, called the elements of the array. In FORTRAN, the subscript notation used for the components of a vector is not available. Instead the elements are denoted by the name of the array followed by an integer expression in parentheses. Thus, the elements of A are denoted by A(1), A(2),... A(N). The parenthesised expressions are called array subscripts even though not written as such.
A subscript can be any arithmetic expression which evaluates to an integer. Thus, if A, B, and C are arrays, the following are valid ways of writing an array element:
A(10) B(I+4) C(3*I+K)
Since an array is a list of variables, it obviously requires several words or other units of storage. Each array must therefore be declared in a statement which tells the compiler how many units to reserve for it. This can be done by including the array name in a type specification followed by its dimension in parentheses. For example:
This reserves 100 words of storage for array AGE, 25 for array NUM, and one word for the variable DEG. All three items are of type INTEGER.
Space can also be reserved for arrays by the DIMENSION statement, which reserves storage using a similar syntax, but includes no information about type. Thus, if this method is used, the type is either determined by the initial letter of the array or assigned by a separate type specification. Therefore, the equivalent to the above using a DIMENSION statement is:
INTEGER AGE,DEG DIMENSION AGE(100),NUM(25)
(NUM is typed as INTEGER by default).
DIMENSION statements, like type specifications, are non-executable and must be placed before the first executable statement.
Since a type specification can stipulate both type and dimension, there is little point in using DIMENSION statements.
When this form of declaration is used in a type or DIMENSION statement the upper and lower bounds for the subscript are 1 and the dimension respectively. Thus, AGE in the above example may have any subscript from 1 to 100. Arrays can also be declared to have subscripts with a lower bound other than 1 by using a second form of declaration in which the lower and upper bounds are given, separated by a colon. For example:
REAL C(0:20) INTEGER ERROR(-10:10)
reserves 21 words of storage for each of the arrays C and ERROR and stipulates that the subscripts of C range from 0 to 20 inclusive, while those of ERROR range from -10 to 10.
Although the declaration stipulates bounds for the subscript, not all compilers check that a subscript actually lies within the bounds. For example, if NUM is declared as above to have a subscript from 1 to 25, a reference to NUM(30)may not cause an error. The compiler may simply use the 30th word of storage starting from the address of NUM(1) even though this is outside the bounds of the array. This can cause unpredictable results. Care should therefore be taken to make sure that your subscripts are within their bounds.
Array elements can be used in the same way as variables, their advantage being that different elements of an array can be referenced by using a variable as a subscript and altering its value, for example by making it the control variable of a DO loop. This is illustrated in the following sections.
The array name without a subscript refers to the entire array and can be used only in a number of specific ways.
Values can be assigned to the elements of an array by assignment statements, e.g.
NUM(1) = 0
NUM(2) = 5
If all the elements are to have equal values, or if their values form a regular sequence, a DO loop can be used. Thus, if NUM and DIST are arrays of dimension 5:
DO 10, I = 1,5 NUM(I) = 0 10 CONTINUE
initialises all the elements of NUM to 0, while:
DO 10, I = 1,5 DIST(I) = 1.5*I 10 CONTINUE
assigns the values 1.5, 3.0, 4.5, 6.0 and 7.5 to DIST(1),DIST(2),DIST(3), DIST(4) and DIST(5) respectively.
The DATA statement is a non-executable statement used to initialise variables. It is particularly useful for initialising arrays. It has the form:
DATA variable_list/constant_list/ [,variable_list/constant_list/] ...
(The square brackets and ellipsis have their usual meaning.)
Each variable_list is a list of variables, and each constant_list a list of constants, separated by commas in each case. Each constant_list must contain the same number of items as the preceding variable_list and corresponding items in sequence in the two lists must be of the same type.
The DATA statement assigns to each variable in each variable_list a value equal to the corresponding constant in the corresponding constant_list. For example:
assigns the values 1.0 and 2.0 respectively to the REAL variables A and B, and 17 to the INTEGER variable N.
A constant may be repeated by preceding it by the number of repetitions required (an integer) and an asterisk. Thus:
assigns a value of zero to each of the variables N1,N2,N3 and N4.
Items in a variable_list may be array elements. Thus, if A is an array of dimension 20, the DATA statement:
assigns a value of zero to the first four elements, -1.0 to the last element, and leaves the remaining elements undefined.
When a large number of array elements have to be initialised, we can avoid writing them all individually by using an implied DO list.
An implied DO list is used in a DATA statement or an input/output statement to generate a list of array elements. The simplest form of implied DO list is:
where dlist is a list of array elements separated by commas. The expresssion: int=c1,c2[,c3] has a similar effect to the expression: var=e1,e2,[,e3] in a DO loop, but int must be a variable of type INTEGER, and c1,c2 and c3 must be constants or expressions with constant operands. The implied DO variable int is defined only in the implied DO list, and is distinct from any variable of the same name used elsewhere.
The implied DO list expands dlist by repeating the list for each value of int generated by the loop, evaluating the array subscripts each time. Thus:
has the same effect as the previous example.
A more complex use of an implied DO list is shown by the example:
which assigns a value of zero to A(1),A(2), A(4),A(5), ... A(19),A(20) and a value of 1.0 to every third element A(3),A(6), ... A(18) .
Finally, an entire array can be initialised by including its name, without a subscript, in variable_list in a DATA statement. This is equivalent to a list of all its elements in sequence. Thus, if A has dimension 20, all the elements of A are initialised to zero by:
DATA statements can be placed anywhere in a program after any specifications. In the interests of clarity, it is probably best to put them immediately before the first executable statement. Wherever they may be, they cause initialisation when the program is loaded (before execution begins). Therefore they can only be used to initialise variables and not to re-assign values to them throughout execution of the program. For this purpose, assignment statements or READ statements must be used.
Array elements and array names can be used in input/output statements in much the same way as in DATA statements. Thus, input and output lists can include:
Implied DO lists in input/output statements differ in two respects from those in DATA statements:
PRINT *, (A(I),'ABC', K, I=1,4)
will print the values of A(1)...A(4) followed in each case by 'ABC' and the value of K.
N = 5 . PRINT *,(A(I),I=1,N)
In an input statement, the loop parameters can depend on values read before by the same statement, e.g.
READ *, N, (A(I),I=1,N)
If variables are used in this way, care should be taken to ensure that they lie within the subscript bounds of the array, as in the following example:
REAL A(20) . READ *, N IF (N.GE.1 .AND. N.LE.20) THEN READ *, (A(I),I=1,N) ELSE PRINT *, N, 'EXCEEDS SUBSCRIPT BOUNDS.' END IF
We can now return to the exam marks problem mentioned at the beginning of the chapter.
Write a program to read the marks of a class of students in five papers, and print, for each paper, the number of students sitting it and the average mark. The marks are to be read as a list of five marks in the same order for each student, with a negative mark if the student did not sit a paper. The end of the data is indicated by a dummy mark of 999.
The outline of the program is:
We shall use arrays MARK, COUNT and TOTAL to store the five marks for a student, a count of students sitting each paper and the total mark for each paper respectively. The program follows.
PROGRAM EXAM INTEGER MARK(5),TOTAL(5),COUNT(5) DATA COUNT/5*0/,TOTAL/5*0/ READ *,(MARK(I),I=1,5) 10 IF (MARK(1).NE.999) THEN DO 20, I=1,5 IF (MARK(I).GE.0) THEN COUNT(I) = COUNT(I)+1 TOTAL(I) = TOTAL(I)+MARK(I) END IF 20 CONTINUE READ *,(MARK(I),I=1,5) GOTO 10 END IF DO 30, I=1,5 IF (COUNT(I).GT.0) THEN AVMARK = 1.0*TOTAL(I)/COUNT(I) C MULTIPLY BY 1.0 TO CONVERT TO REAL AND AVOID TRUNCATION PRINT *,COUNT(I),' STUDENTS SAT PAPER NUMBER',I PRINT *,'THE AVERAGE MARK WAS', AVMARK ELSE PRINT *,'NO STUDENTS SAT PAPER NUMBER',I END IF 30 CONTINUE END
Figure 12: Exam marks program
One problem with this program is that if the last line of input consists of the single terminating value of 999, the statement: READ *,(MARK(I),I=1,5) will wait for another four values to be entered. This can be avoided by following 999 by a '/' character, which is a terminator causing the READ statement to ignore the rest of the input list.
Suppose now that the exam marks program is to be altered to print a list of all the marks in each paper, with the differences between each mark and the average for the paper. This requires that all the marks should be stored. This could be done by making the dimension of MARK large enough to contain all the marks, and reserving the first five elements for the first student's marks, the next five for the second student's marks and so on. This would be rather awkward.
The problem could be dealt with more easily if we could add a second subscript to the MARK array to represent the number of each student in sequence. Our array could then be declared either by:
and would reserve enough space to store the marks of up to 100 students in 5 subjects.
In fact, FORTRAN arrays can have up to seven dimensions, so the above declarations are valid. The subscript bounds are specified in the same way as for one-dimensional arrays. For example:
declares a three-dimensional array of type REAL, with subscript bounds of 1...5, 0...5 and -10...10 in that order.
An array element must always be written with the number of subscripts indicated by the declaration.
When multi-dimensional array elements are used in an implied DO list, multiple subscripts can be dealt with by including nested implied DO lists in dlist, for example:
READ *, (A(J),(MARK(I,J),I=1,5),J=1,100)
Here, dlist contains two items, A(J) and the implied DO list (MARK(I,J),I=,5) .This inner implied DO list is expanded once for each value of J in the outer implied DO list. Thus the above READ statement reads values into the elements of A and MARK in the order:
A(1), MARK(1,1), MARK(2,1),... MARK(5,1) A(2), MARK(1,2), MARK(2,2),... MARK(5,2) . A(100),MARK(1,100),MARK(2,100),...MARK(5,100)
The unsubscripted name of a multi-dimensional array can be used, like that of a one-dimensional array, in input/output and DATA statements to refer to all its elements, but it is essential to know their order. The elements are referenced in the order of their positions in the computer's memory. For a one-dimensional array, the elements occur, as we might expect, in increasing order of their subscripts, but for multi-dimensional arrays, the ordering is less obvious. The rule is that the elements are ordered with the first subscript increasing most rapidly, then the next and so on, the last subscript increasing most slowly. Thus if MARK is declared as:
its elements are ordered in memory as shown above, and the statement:
is equivalent to:
READ *, ((MARK(I,J),I=1,5),J=1,100)
Of course, the order could be altered by swapping the control variables in the inner and outer implied DO loops thus:
READ *, ((MARK(I,J),J=1,100),I=1,5)
We can use a two-dimensional array to solve the problem posed at the beginning of this section.
Write a program to read the marks of up to 100 students in five papers, and print, for each paper, the number of students sitting it, the average mark, and a list of the marks and their differences from the average. The marks are to be read as a list of five marks in the same order for each student, with a negative mark if the student did not sit a paper. The end of the data is indicated by a dummy mark of 999.
The outline is:
Step 4.1.3 can be further outlined as:
If his/her mark in the paper is not negative, then:
Since the marks are read five subjects at a time for each student, it is convenient to store them in an array MARK(5,100). The program follows:
PROGRAM EXAM2 INTEGER MARK(5,100),TOTAL(5),COUNT(5),ALL DATA COUNT/5*0/,TOTAL/5*0/,ALL/0/ DO 20, J=1,100 READ *,(MARK(I,J),I=1,5) IF (MARK(1,J).EQ.999) GOTO 30 ALL = ALL+1 DO 10, I=1,5 IF (MARK(I,J).GE.0) THEN COUNT(I) = COUNT(I)+1 TOTAL(I) = TOTAL(I)+MARK(I,J) END IF 10 CONTINUE 20 CONTINUE READ *,LAST IF (LAST.NE.999) THEN PRINT *,'MARKS ENTERED FOR MORE THAN 100 STUDENTS.' STOP END IF 30 DO 50, I=1,5 IF (COUNT(I).GT.0) THEN AVMARK = 1.0*TOTAL(I)/COUNT(I) C MULTIPLY BY 1.0 TO CONVERT TO REAL AND AVOID TRUNCATION PRINT *,COUNT(I),' STUDENTS SAT PAPER NUMBER',I PRINT *,'THE AVERAGE MARK WAS', AVMARK PRINT *,'MARKS AND THEIR DIFFERENCES FROM THE AVERAGE:' DO 40, J=1,ALL IF (MARK(I,J).GE.0)PRINT *,MARK(I,J),MARK(I,J)-AVMARK 40 CONTINUE ELSE PRINT *,'NO STUDENTS SAT PAPER NUMBER',I END IF 50 CONTINUE END
Figure 13: Exam marks program (version 2)
[Contents] [Previous] [Next] [Home]
webmaster Massimo F. ARENA