Copyright (c) 1990, 1994 W. Brainerd, C. Goldberg, and J. Adams. All rights reserved. This file may not be copied without permission of the authors.
In ordinary usage, a list is a sequence of values, usually all representing data of the same kind, or otherwise related to one another. A list of students registered for a particular course and a list of all students enrolled at a college are examples.
In Fortran, a collection of values of the same type is called an array. We will also refer to a one-dimensional array as a list.
Frequently, the same operation or sequence of operations
is performed on every element in an array.
On a computer that performs one statement at a time,
it makes sense to write such programs by specifying what happens
to a typical element of the array and enclosing these statements
in a sufficient number of
do constructs (loops)
to make them apply to every element.
Fortran 90 also has powerful intrinsic operations and functions
that operate on whole arrays or sections of an array.
Programs written using these array operations are often clearer and
are more easily optimized by Fortran compilers.
Especially on computers with parallel or array processing capabilities,
such programs are more likely to take advantage of the special hardware
to increase execution speed.
We introduce the use of arrays with an example involving credit card numbers.
As an example of a problem concerned with a list, suppose that a company maintains a computerized list of credit cards that have been reported lost or stolen or that are greatly in arrears in payments. The company needs a program to determine quickly whether a given credit card, presented by a customer wishing to charge a purchase, is on this list of credit cards that can no longer be honored.
Suppose that a company has a list of 8262 credit cards reported lost or stolen, as illustrated in Table 4-1.
Table 4-1 Lost credit cards.
Account number of 1st lost credit card 2718281 Account number of 2nd lost credit card 7389056 Account number of 3rd lost credit card 1098612 Account number of 4th lost credit card 5459815 Account number of 5th lost credit card 1484131 . . . . . . Account number of 8262nd lost credit card!1383596
Since all of the 8262 numbers in the list must be retained simultaneously in the computer's main memory for efficient searching, and since a simple (scalar) variable can hold only one value at a time, each number must be assigned as the value of a variable with a different name so that the computer can be instructed to compare each account number of a lost or stolen card against the account number of the card offered in payment for goods and services.
It is possible to use variables with the 8262 Fortran names
lost_card_1 lost_card_2 lost_card_3 . . . lost_card_8262
to hold the 8262 values.
Unfortunately, the Fortran language does not recognize the intended
relationship between these variable names,
so the search program cannot be written simply.
The Fortran solution is to declare a single object name
that consists of many individual integer values.
The entire collection of values may be referenced
by its name
lost_card and individual card numbers in the list
may be referenced by the following names:
lost_card (1) lost_card (2) lost_card (3) . . . lost_card (8262)
This seemingly minor modification of otherwise perfectly acceptable variable names opens up a new dimension of programming capabilities. All the programs in this chapter, and a large number of the programs in succeeding chapters, use this form.
The numbers in parentheses that specify the location of an item
within a list are
a name borrowed from mathematics.
Although mathematical subscripts are usually written below the line
(hence the name),
such a form of typography is impossible on most computer input devices.
A substitute notation, enclosing the subscript in parentheses or brackets,
is adopted in most computer languages.
It is customary to read the expression
``x sub 3'',
just as if it were written
The advantage of this method of naming the quantities over using the variable names lost_card_1, lost_card_2, ..., lost_ card_8262 springs from the following programming language capability: The subscript of an array variable may itself be a variable, or an even more complicated expression.
The consequences of this simple statement are much more profound than would appear at first sight.
For a start in describing the uses of a subscript that is itself a variable, the two statements
i = 1 print *, lost_card (i)
produce exactly the same output as the single statement
print *, lost_card (1)
namely, 2718281, the account number of the first lost credit card on the list.
The entire list of account numbers of lost credit cards can
be printed by the subroutine
subroutine print_lost_cards (lost_card) integer, dimension (1:8262), intent (in) :: lost_card integer :: i do i = 1, 8262 print *, lost_card (i) end do end subroutine print_lost_cards
As an example of an array feature in Fortran, the collection of card numbers as a whole can be referenced by the one statement
print *, lost_card
The replacement just made actually creates a different output.
The difference is that using the
to execute a
print "(4i8)", lost_card
This is a little better, but another problem is that the number of lost and stolen cards varies daily. The subroutine will not be very useful if it makes the assumption that there are exactly 8262 cards to be printed. This can be fixed easily by changing the declaration to
integer, dimension (:), intent (in) :: lost_cardThe colon indicates that the size of the array
lost_cardis to be assumed from the array that is the actual argument given when the subroutine is called. Also, this passed-on size is used in the
The name of an array must obey the same rules as an ordinary variable name. Each array must be declared in the declaration section of the program. A name is declared to be an array by putting the dimension attribute in a type statement followed by a range of subscripts, enclosed in parentheses. For example,
real, dimension (1 : 9) :: x, y logical, dimension (-99 : 99) :: yes_no
y are lists of 9 real values
yes_no is a list of 199 logical values.
These declarations imply that a subscript for
y must be an integer
expression with a value from 1 to 9 and
that a subscript for yes_no Must be an integer
expression whose value is from -99 to +99.
A list of character strings may be declared in a form like the following:
character (len = 8), dimension (0 : 17) :: char_list
In this example, the variable
char_list is a list of 18 character strings,
each of length 8.
of an array is a list of the number of elements in each dimension.
A 9 x 7 array has shape (9,7);
char_list declared above has shape (18);
and the array declared by
integer, dimension (9, 0:99, -99:99) :: iii
has shape (9,100,199).
When only one number is given in a
dimension declaration in place
of a subscript range, it is used as the upper subscript bound
and the lower bound is 1.
The shape of a scalar is a list with no elements in it.
The shape of a scalar or array can be computed
shape intrinsic function.
real, dimension (:, :), allocatable :: a, b
indicates that the arrays
B have two dimensions (rank 2).
The colons mean that
the extents along each dimension will be established later,
allocate statement is executed.
A similar declaration using colons may occur for a dummy argument of a procedure, indicating that the shape of the dummy array is to be taken from the actual argument used when the procedure is called. This sort of dummy argument is called an assumed-shape array.
subroutine s (d) integer, dimension (:, :, :) :: d
allocatable attribute must not be used for an array argument,
pointer attribute may be (see chapter 8).
The declaration of arrays also may use values of other dummy arguments to establish extents; such arrays are called automatic arrays. For example, the statements
subroutine s2 (dummy_list, n, dummy_array) real, dimension (:) :: dummy_list real, dimension (size (dummy_list)) :: local_list real, dimension (n, n) :: dummy_array, local_array real, dimension (2 * n + 1) :: longer_local_list
declare that the size of
dummy_list is to be the
same as the size of the corresponding actual argument,
that the array
local_list is to be the same size as
local_array are both
to be two-dimensional arrays with
The last declaration shows that some arithmetic
on other dummy arguments is permitted in calculating array bounds;
these expressions may include references to certain intrinsic functions,
In the main program, an array must either be declared with constant
fixed bounds or be declared
allocatable and be given bounds
by the execution of an
allocate statement (4.1.5).
In the first case, our lost and stolen card program might contain
integer, dimension (8262) :: lost_card
This is not satisfactory if the number of lost cards changes frequently.
In this situation, perhaps the best solution is to declare the array
to have a sufficiently large upper bound so that there will always
be sufficient space to hold the card numbers.
Because the upper bound is fixed, there must be a variable whose value is the
actual number of cards lost.
Assuming that the list of lost credit cards
is stored in a file connected to the standard input unit (UNIT=*),
the following program fragment reads, counts, and prints
the complete list of lost card numbers.
read statement has an
iostat keyword argument
whose value is set to zero if no error occurs
and is set to a negative number if there is an attempt
to read beyond the last data item in the file.
The slightly different form of the
read statement necessitated
by the use of
iostat is described in detail in Section 9.3,
as is the
integer, dimension (20000) :: lost_card integer :: number_of_lost_cards, i, iostat_var do i = 1, 20000 read (unit = *, fmt = *, iostat = iostat_var) & lost_card (i) if (iostat_var < 0) then number_of_lost_cards = i - 1 exit end if end do . . . print "(4i8)", lost_card (1:number_of_lost_cards)
Although the array
lost_card is declared to have room for 20,000 entries,
(see Section 4.1.6 for more details about this notation).
Rather than assign array values one by one, it is convenient to give an array a set of values using an array constructor. An array constructor is a sequence of scalar values defined along one dimension only. An array constructor is a list of values, separated by commas and delimited by the pair of two-character symbols ``(/'' and ``/)''. There are three possible forms for the array constructor values:
X (1:4) = (/ 1.2, 3.5, 1.1, 1.5 /)
X (1:4) = (/ A (I, 1:2), A (I+1, 2:3) /)
doloop as in
x (1:4) = (/ (sqrt (real (i)), i = 1, 4) /)
If there are no values specified in an array constructor,
the resulting array is zero sized.
The values of the components must have the same type and type parameters
(kind and length).
The rank of an array constructor is always one;
reshape intrinsic function can be used
to define rank-two and greater arrays from the array constructor values.
reshape ( (/ 1, 2, 3, 4, 5, 6 /), (/ 2, 3 /) )
is the 2 x 3 array
1 3 5 2 4 6
is a list of expressions, followed by something that is like
an iterative control in a
The whole thing is contained in parentheses.
It represents a list of values
obtained by writing each member of the list once for each value
do variable replaced by a value.
For example, the implied
do list in the array constructor above
(sqrt (real (i)), i = 1, 4)
is the same as the list
sqrt (real (1)), sqrt (real (2)), & sqrt (real (3)), sqrt (real (4))
print *, (a (i, i), i = 1, n)
In Fortran 77, all the storage that was required during execution of a program could be determined and allocated by the compiler. This is known as static storage allocation, as opposed to dynamic storage allocation, which means that storage may be allocated or deallocated during execution of the program.
With static allocation, the size of each array must be declared at compile time, usually as the largest size anticipated in any execution of the program. With dynamic storage allocation, the program can wait until it knows during execution exactly what size array is needed and then allocate only that much space. Memory also can be deallocated dynamically, so that the storage used for a large array early in the program can be reused for other large arrays later in the program after the values in the first array are no longer needed.
For example, instead of relying on an end-of-file condition when reading in the list of lost cards, it is possible to keep the numbers stored in a file with the number of lost cards as the first value in the file, such as
8262 2718281 7389056 1098612 5459815 1484131 . . . 1383596
The program can then read the first number, allocate the correct amount of space for the array, and read the lost card numbers.
integer, dimension (:), allocatable :: lost_card integer :: number_of_lost_cards . . . ! The first number in the file is ! the number of lost card numbers in the ! rest of the file. read *, number_of_lost_cards allocate (lost_card (number_of_lost_cards)) ! Read the numbers of the lost cards read "(i7)", lost_card . . .
In the declaration of the array
the colon is used to indicate
the rank (number of dimensions) of the array, but the bounds
are not pinned down until the
allocate statement is executed.
Because the programmer doesn't know how many lost cards there will be,
there is no way to tell the compiler that information.
During execution, the system must be able to create an array
of any reasonable size
reading from the input data file the value of the variable
may be used to free the allocated storage.
Pointers, discussed in Chapter 8, provide an additional and more general facility for constructing data structures with variable sizes.
In the following statement, used in the example that tests for
an end-of-file condition, a section of the array
lost_card is printed.
print "(4i8)", lost_card (1:number_of_lost_cards)
On many occasions such as the one above, only a portion of the elements of an array is needed for a computation. It is possible to refer to a selected portion of an array, called an array section. A parent array is an aggregate of array elements, from which a section may be selected.
In the following example
real a (10) . . . a (2:5) = 1.0
the parent array
a has 10 elements.
The array section consists of elements
The section is an array itself and the value 1.0 is assigned
to all four of the elements in
In addition to the ordinary subscript that can select a subobject of an array, there are two other mechanisms for selecting certain elements along a particular dimension of an array. One is a subscript triplet, an example of which was shown above, and the other is a vector subscript.
The syntactic form of a subscript triplet is
[ expression ] : [ expression ] [ : expression ]
where each set of brackets encloses an optional item and each expression must produce a scalar integer value. The first expression gives a lower bound, the second an upper bound, and the third a stride. If the lower bound is omitted, the lower bound that was declared or allocated is used. (Note that an assumed-shape dummy array is treated as if it were declared with lower bound 1.) If the upper bound is omitted, the upper bound that was declared or allocated is used. The stride is the increment between the elements in the section referenced by the triplet notation. If omitted, it is assumed to be one. For example, if V is a one-dimensional array (list) of numbers:
Each expression in the subscript triplet must be scalar. The values of any of the expressions in triplet notation may be negative. The stride must not be zero. If the stride is positive, the section is from the first subscript up to the second in steps of the stride. If the stride is negative, the section is from the first subscript down to the second, decrementing by the stride.
Another way of selecting a section of an array is to use a vector subscript.
is an integer array expression of rank one.
For example, if
iv is a list of three integers, 3, 7, and 2,
x is a list of 9 real numbers 1.1, 2.2, ..., 9.9,
the value of
x (iv) is the list of three numbers 3.3, 7.7, and 2.2-the
third, seventh, and second elements of
Ordinary subscripts, triplets, and vector subscripts may be mixed in selecting an array section from a parent array. An array section may be empty.
Consider a more complicated example.
b were declared in a type statement as
real b (10, 10, 5)
b (1:4:3, 6:8:2, 3)
is a section of
b, consisting of four elements:
b (1, 6, 3) b (1, 8, 3) b (4, 6, 3) b (4, 8, 3)
The stride along the first dimension is 3;
therefore, the notation references the first subscripts 1 and 4.
The stride in the second dimension is 2,
so the second subscript varies by 2 and takes on values 6 and 8.
In the third dimension of
b, there is no triplet notation, so the
third subscript is 3 for all elements of the section.
The section would be one that has a shape of (2, 2), that is,
it is two dimensional, with extents 2 and 2.
To give an example using both triplet notation and a vector subscript,
suppose again that
b is declared as above:
real b (10, 10, 5)
b(8:9, 5, (/ 4, 5, 4/) ) is a 2 x 3 array
consisting of the six values:
b (8, 5, 4) b (8, 5, 5) b (8, 5, 4) b (9, 5, 4) b (9, 5, 5) b (9, 5, 4)
vs is a list of three integers,
vs = (/ 4, 5, 4 /),
b(8:9, 5, vs) would have the same value.
b(8:9, 5, vs) cannot occur on the left side
of an assignment because of the duplication of elements of
Array assignment is permitted under two circumstances: when the array
expression on the right has exactly the same shape as the array
on the left, or when the expression on the right is a scalar.
Note that, for example, if
a is a 9 x 9 array,
a (2:4, 5:8) is the same shape as
a (3:5, 1:4),
so the assignment
a (2:4, 5:8) = a (3:5, 1:4)
is valid, but the assignment
a (1:4, 1:3) = a (1:3, 1:4)
is not valid because even though there are 12 elements in the array on each side of the assignment, the left side has shape 4 x 3 and the right side has shape 3 x 4.
When a scalar is assigned to an array, the value of the scalar is assigned to every element of the array. Thus, for example, the statement
m (k+1:n, k) = 0
sets the elements
m (k+1, k),
m (k+2, k), ...,
m (n, k) to zero.
whereStatement and Construct
may be used to assign values to only those
elements of an array where a logical condition is true.
For example, the following statement sets the elements of
b to zero
in those positions where the corresponding element of
a is negative.
The other elements of
b are unchanged.
b must be arrays of the same shape.
where (a < 0) b = 0
The logical condition in parentheses is an array of logical values conformable to each array in the assignment statement. In the example above, comparison of an array of values with a scalar produces the array of logical values.
permits any number of array assignments to be done under
control of the same logical array, and the
permits array assignments to be done
where the logical expression is false.
The following statements assign to the array
a the quotient of the
corresponding elements of
c in those cases where the
c is not zero.
In the positions where the element of
c is zero,
the corresponding element of
a is set to zero and the zero elements
c are set to 1.
where (c /= 0) ! c/=0 is a logical array. a = b / c ! a and b must conform to c. elsewhere a = 0 ! The elements of a are set to 0 ! where they have not been set to b/c. c = 1 ! The 0 elements of c are set to 1. end where
where statement or
only array assignments are permitted.
The shape of all arrays in the assignment statements
must conform to the shape of the logical expression
following the keyword
The assignments are executed in the order they are written-first
those in the
where block, then those in the
where constructs may not be nested.
All of the intrinsic operators and many of the intrinsic functions
may be applied to arrays,
operating independently on each element of the array.
For example, the expression
abs (a (k:n, k)) results in a one-dimensional
n-k+1 nonnegative real values.
A binary operation, such as
*, may be applied only
to two arrays of the same shape,
in which case it multiplies corresponding elements of the two arrays.
The assignment statement
a (k, k:n+1) = a (k, k:n+1) / pivot
divides each element of
a (k, k:n+1) by the real scalar value
In essence, a scalar value may be considered an array
of the appropriate size
with all its entries equal to the value of the scalar.
y (0:7) + z (-7:0), which results in an array whose eight values are considered to have subscripts 1, 2, 3, .., 8.
The renumbering must be taken into account when referring back to the
v is a one-dimensional integer array
that is given an initial value with the declaration:
integer, dimension (0:6), parameter :: & v = (/ 3, 7, 0, -2, 2, 6, -1 /)
The intrinsic function
maxloc returns a list of integers
giving the position of the largest element of an array.
maxloc (v) is (/ 2 /) because position 2 of the list
the largest number, 7, even though it is
v (1) that has the value 7.
maxloc (v (2:6)) is the list (/ 4 /)
because the largest entry, 6, occurs in the fourth position
in the section
There is also an intrinsic function,
minloc, whose value is
the list of subscripts of a smallest element of an array.
For example, if
1 8 0 5 -1 7 3 9 -2
the value of
minloc (a) is (/3, 3/) because
a (3, 3) is
the smallest element of the array.
valuesto be an array of 100 real values with subscripts ranging from -100 to -1.
squares. For example,
squares (5)= 25.
character (len = 1), dimension (8, 8) :: board
board = "W"
assigns the color white to all 64 positions.
Write a statement or statements that assigns ``B''
to the 32 black positions.
board (1, 1) is to be white so that the board
is as shown in Figure 4-1.
W B W B W B W B B W B W B W B W W B W B W B W B B W B W B W B W W B W B W B W B B W B W B W B W W B W B W B W B B W B W B W B W
Figure 4-1 A chess board.
listis a one-dimensional array that contains
max_sizereal numbers in ascending order. Write a subroutine
insert (list, n, max_size, new_entry)that adds the number
new_entryto the list in the appropriate place to keep the entire list sorted.
sqrt(V . V) where . is the vector dot product. The cosine of the angle between V1 and V2 is given by
V1 . V2 cos (theta) = _____________ |V1| |V2|acos (arccosine) may be used to find an angle with a given cosine.