Arrays

Yutaka Masuda

February 2020

Back to index.html.

Arrays

Use of 1-dimensional array

Array basics

Small examples

Fortran can manage a set of variables by a single name. In other words, a single object can have multiple variables with the same type. This object is called array, and each variable in the array is element. The array is often explained as a 1-dimensional structure with multiple variables.

This structure is useful to define a vector that has multiple elements. The array is not exclusively used in the vector. It is useful whenever you need multiple (possibly many) variables.

The following program defines an integer array with 3 elements and assigns a value to each element. Equivalently, you can see it as a vector with 3 elements.

program arr
   implicit none
   integer :: vec(3)
   vec(1) = 32
   vec(2) = 128
   vec(3) = -1024
   print *,vec
end program arr
          32         128       -1024

You can see the essential feature of an array in this example.

The subscript 3 in the above example means the number of elements and the last subscript (index) because the subscript starts from 1. You may feel uncomfortable of this system if you like C or Python which has the 0-origin subscript. Fortran is a language of 1-origin subscript, and it is useful to describe mathematical algorithms.

When an array is declared, its elements are not defined. So, we do not expect that a just-declared array has particular numbers. It is the same as a variable, and the programmer is responsible for assigning some values to the array before using it.

Contrary to R or Python, in Fortran, the array holds only a particular type of data declared in the program. In the above example, integer::vec(3) can contain only integer numbers. With this limitation, Fortran can efficiently manipulate the arrays.

It is still possible to develop a system in Fortran to hold the various data types in a single array in Fortran (like data.frame in R) if you develop it from scratch (or look for existing programs to do this). Or, you just avoid to deal with such a flexible array at a time, look for a workaround to use simple arrays. For example, if you should hold both characters and numbers, you can replace all the characters with sequential integer code, and all the information is represented as numbers.

Another way of declaration

There is another way to declare an array: the dimension attribute. The following piece of program is equivalent to integer::vec(3).

program arr
   implicit none
   integer,dimension(3) :: vec
end program arr

In my personal opinion, this style is less intuitive than vec(3) to declare a 3-element array. I will not use dimension in the following sections.

Arbitrary subscript

You can use any range of subscript in the array. The range should be declared as (stard index:end index) in the declaration statement. You can not use real numbers a subscript in the array. For example, the following program supports the range \(-1\) to \(2\) for the array.

program arr
   implicit none
   integer :: vec(-1:2)
   vec(-1) = 32
   vec(0) = 128
   vec(1) = -1024
   vec(2) = 12345
   print *,vec
end program arr

Using this feature, you can create a 0-origin subscript easily. Nevertheless, you should not abuse this feature because of a lack of readability.

Array section (sub-array)

You can extract a section of an array and use it as a separate array, like a section of characters. The following program shows only the 2nd and the 3rd elements.

program arr
   implicit none
   integer :: vec(3)
   read *,vec
   print *,vec(2:3)
end program arr

The sub-array is defined with a range, (start index:end index), instead of a single subscript. In the above case, it shows only the elements 2 and 3. The index system is very similar to for characters.

Assignment to array

Literal arrays

There are several options to assign literal values to an array. The easiest way is to put a single value to each element, as shown in the previous example. Another way assigns multiple literal-values to the array at the same time. The following program has the same results as the previous one.

program arr
   implicit none
   integer :: vec(3)
   vec = [32, 128, -1024]
   print *,vec
end program arr

The multiple literals are encircled with [ and ], and its number of elements should be the same as the array variable. Similar to a single variable, the initialization can be done in the declaration statement.

program arr
   implicit none
   integer :: vec(3) = [32, 128, -1024]
   print *,vec
end program arr

When assigning multiple-literals, you have to check the size of the data. For example, the following statement fails.

   vec(3) = [32, 128, -1024]

The brackets [ and ] to define an array was introduced in Fortran 2003. Traditionally, a combination of a slash symbol and parentheses, (/ and /), was used (and is still used). The above code looks like this using the traditional symbols.

   vec = (/32, 128, -1024/)
Literal scalar

When you assign a scalar to an array, all elements of the array are the scalar value. It is useful to initialize an array with a particular value.

program arr
   implicit none
   integer :: vec(3)
   vec = 0
   print *,vec
end program arr
         0         0         0
Substitution

If you have two arrays with the same size, you can directly substitute one to another.

program arr
   implicit none
   integer :: a(3),b(3)
   a = [1,2,3]
   b = a
end program arr

If the arrays have different sizes, the compiler may fail (or, it passes, but the executable may fail in run time).

Reading from keyboard

The multiple values can also be from the keyboard with the read statement. It reads multiple values at the same time as multiple variables are specified.

program arr
   implicit none
   integer :: vec(3)
   read *,vec
   print *,vec
end program arr

Unknown size of an array

In many cases, the size of the vector (array) is unknown at the beginning of the program, but it will be determined in the program. Fortran has an allocatable array which size is not defined in the declaration statement but fixed in the program. The following example is for an allocatable array.

program arr
   implicit none
   integer,allocatable :: vec(:)
   integer :: n
   n = 5
   allocate(vec(n))
   read *,vec
   print *,vec
   deallocate(vec)
end program arr

You can see the usage of allocatable arrays.

Precisely, allocate reserves the storage in memory and assign it to the array. If the memory requirement is too large, the program stops with an error. If successful, the array is tied with the allocated memory with the specified size. The deallocate statement releases the allocated memory, and the status of an array is back to non-allocated. If you want to resize the array, you have to deallocate it once, then call allocate with the new size (it was relaxed in Fortran 2003, see the later chapter). When the program stops, regardless of normal end or error stop, all the allocated arrays will be automatically deallocated, so you should not be concerned about the arrays.

The allocated array initially has nonsense values i.e., uninitialized. The initialization of the array is the responsibility of the programmer.

There is an intrinsic function to test if an allocatable array has already allocated or not.

Function Description Example
allocated() .true. if an allocatable array is allocated allocated(x)

Summary

Exercises

  1. Define integer,parameter::n=5 and use it to specify the size of an array. See if it works when the definition of n is changed to integer::n=5 is used.
  2. Define a real array with 5 elements, read the values from a keyboard, swap between 1st and the 3rd elements, and between 2nd and 4th elements, then print all the elements.
  3. Create a program to read an integer \(n\), allocate an array with \(n\) elements, read the values to this array, and print the values.

Array operations

Array arithmetic

One of the advantages of using arrays is to make array operations much more straightforward than a single scalar variable. Primarily if you treat an array as a vector, the vector operation is simply written like a mathematical formula. The following program calculates a sum of two vectors (i.e. \(\mathbf{c}=\mathbf{a}+\mathbf{b}\)).

program arr
   implicit none
   integer :: a(3),b(3),c(3)
   a = [1,2,3]
   b = [4,5,6]
   c = a + b
   print *,c
end program arr
        5         7         9

The arithmetic operators such as +, -, *, /, and ** perform element-by-element operations for arrays. Note that * does not mean the matrix multiplication but the element-by-element multiplication.

When the operation involves both array and scalar, the scalar will be broadcasted to all the elements in the array. See the following example.

program arr
   implicit none
   integer :: a(3)
   a = [1,2,3]
   print *,a+5
end program arr
        6         7         8

By the way, the above code may secretly allocate a temporary array to store the result of a+5 as a single variable. In the current computers, it should not be a performance bottleneck.

Elemental functions

The element-wise operation is also applied in mathematical functions. For example, sin(x) for the array x returns the array with the sine of each element in x. The size of the return value is the array with the same size as input.

program arr
   implicit none
   real :: x(3)
   x = [1.0, 1.1, 1.2]
   print *,sin(x)
end program arr

Such elemental functions include sin, cos, tan, log, exp, abs, sqrt, and so on.

Logical array

As the arithmetic operations and elemental functions, the logical operations can be applied to an array, and the result is also the array with the same size as input. The following example shows that the resulting array can be logical and stored in an array.

program arr
   implicit none
   real :: x(3)
   logical :: status(3)
   x = [1.0, 1.1, 1.2]
   print *,x<=1.1    ! determine in each element
   status = x<=1.1   ! can be saved to a logical array
   print *,status
end program arr
 T T F
 T T F

Array functions

Fortran has many functions to summarize or to inquire about an array. A typical function is sum(a) which computes the summation of elements in the array a. The function size(a) returns the size (the number of elements) of an array. The following table shows some useful functions.

Function Effect
size(x) the size of x
sum(x) the sum of all elements
minval(x) the minimum value in x
maxval(x) the maximum value in x
minloc(x) the location of the minimum value
maxloc(x) the location of the maximum value
dot_product(x,y) the sum of square i.e. \(\mathbf{x}'\mathbf{y}\)
count(a) the number of .true. in the logical array a
all(a) .true. if all elements in logical a is true
any(a) .true. if any elements in logical a is true

Most of the above functions can accept a mask vector, as a second argument with a mask= identifier, defining which elements are involved in the computation. The mask vector is logical, and it has the same size as the input vector. If an element of the mask is .true., the corresponding input is used, and if .false., the corresponding input is ignored.

For example, the following example computes the sum of an array using only positive numbers.

program arr
   implicit none
   real :: x(3)
   logical :: status(3)
   x = [1.0, -2.0, 3.0]
   print *,sum(x,x>0.0)

   ! equivalent
   status = x>0
   print *,sum(x,mask=status)
end program arr

Summary

Exercises

  1. See what happens when incompatible sized arrays are involved in an arithmetic operation.
  2. Read 5-element integer array and compute the average of the values.
  3. Modify the above program to compute the average only from positive input values.
  4. Compute the standard deviation of a given real array.
  5. Compute the figure-skating average of a real array. This average is calculated from the data in which the highest and the lowest values have been discarded.

Multi-dimensional array

2-dimensional array

Fortran can take care of higher dimensional arrays. The 2-dimensional array is often used as a matrix (and the 1-dimensional array is for a vector). Fortran is designed to efficiently manipulate the arrays (i.e., vectors and matrices). Here we deal only with the 2-dimensional array. See the other textbooks for details of higher-order arrays.

The higher-dimensional array has the same usage as the 1-dimensional array in deceleration, array functions, subarrays, allocation, and so forth. The only difference is the subscript (index) to specify the element. We will look at the basic features of a 2-dimensional array in the next sections.

Decrelation

The following code defines a \(2 \times 2\) array (matrix), and each of4 elements has a value.

program arr
   implicit none
   integer :: mat(2,2)
   mat(1,1) = 10
   mat(1,2) = 20
   mat(2,1) = 30
   mat(2,2) = 40
   print *,mat
end program arr
          10          30          20          40

The above array defines the following matrix. \[ \left[ \begin{array}{cc} 10&20 \\ 30&40 \end{array} \right] \] The usage of 2-dimensional array is similar to a vector except for subscripts (indices). The matrix has 2 indices in () separated by a comma. The first index is for ow, the second is for column same as the mathematical notation like \(A_{ij}\) where \(i\) is for row and \(j\) is for a column.

The multi-dimensional array can be defined with dimension. We will not use this method in this tutorial.

   integer,dimention(2,2) :: mat

Order in memory

In the previous example, you may notice that the order of elements in output is not intuitive. A computer should rearrange the multi-dimensional data into a 1-dimensional structure in memory. In Fortran, the 2-dimensional array is treated as a collection of column vectors (so-called the column-major storage).

\[ \left[ \begin{array}{c|c} 10&20 \\ 30&40 \end{array} \right] \rightarrow \left[ \begin{array}{c} 10\\ 30\\ \hline 20\\ 40 \end{array} \right] \]

This rearrangement (column-major) is reasonable in mathematics; R is also using this order. However, it is different from C and some other languages (row-major), and when you come from such languages, please be careful about the array rearrangement in memory.

The order becomes an issue when you read a matrix from the keyboard.

program arr
   implicit none
   integer :: mat(2,2)
   read *,mat
   print *,mat
end program arr

If you type 1 2 3 4 in terminal, the matrix will be as follows. \[ \left[ \begin{array}{cc} 1&3 \\ 2&4 \end{array} \right] \]

If you want to access the matrix along with rows, you have to write loops (see exercises).

Inquery functions for array shape

Fortran has several functions to get the size of an array.

Function Effect Return value
size(x) the size of x the number of all elements in x
shape(x) the dimention of x a vector with the length of the dimention

For example, using the \(2 \times 3\) matrix, the above functions return the following values.

program arr
   implicit none
   integer :: a(2,3)
   print *,size(a)
   print *,shape(a)
end program arr

The size function can get the number of elements in a row (or column) as scalar using the 2nd parameter.

program arr
   implicit none
   integer :: a(2,3)
   print *,size(a,1)   ! dimension 1 = elements within column
   print *,size(a,2)   ! dimension 2 = elements within row
   print *,size(a)     ! all elements
end program arr
         2
         3
         6

Assignment to array

Literals

The assignment of literal values to a 2-dimensional array (hereafter a matrix) is not straightforward because we have to consider the dimension. There are several ways to assign values to a matrix. In the following example, we try to have the following \(2 \times 2\) matrix.

\[ \left[ \begin{array}{cc} 10&20 \\ 30&40 \end{array} \right] \]

The Fortran program shows 3 different ways to do it.

program arr
   implicit none
   integer :: mat(2,2)

   ! method 1
   mat(1,1:2) = [10, 20]
   mat(2,1:2) = [30, 40]

   ! method 2
   mat(1:2,1) = [10, 30]
   mat(1:2,2) = [20, 40]

   ! method 3
   mat = reshape([10,30,20,40],shape(mat))
end program arr

The first method substitute rows to the matrix. The second is the same way as first but in a column-wise manner. The third uses the reshape function, which rearranges a vector to fit the matrix with a given shape. Using reshape, you should consider the column-major order of elements to be aligned in the matrix.

Function Effect Return value
reshape(x,s) rearrange x with shape s rearranged array

The 3rd method is the same as reshape([10,30,20,40],[2,2]) instead of using shape(mat).

Other values

You can assign a scalar to an array so that all the elements have the same value. It is useful to initialize an array. You can also assign a (sub)array to another array.

! for exercise 1
program arr
   implicit none
   integer :: a(3,3),b(2,2)

   a = reshape([1,2,3,4,5,6,7,8,9],shape(a))
   b = 0
   b = a(2:3,1:2)
end program arr

Computations

Element-by-element operations

As 1-dimensional arrays, higher-dimensional arrays can be involved in simple arithmetic such as + and *. Also, most of the mathematical functions like log() can be applied to all elements at the same time.

! for exercise 2
program arr
   implicit none
   integer :: a(2,2),b(2,2),c(2,2)

   a = reshape([1,2,3,4],shape(a))
   b = reshape([5,6,7,8],shape(b))

   ! element-by-element product
   c = a * b
   print *,c
end program arr
Matrix multiplication and manipulation

In Fortran, the function for matrix multiplication matmul is available by default. It accepts 2 arrays, and compute the product, and return the result as a matrix. If the size of matrices is unconformable, the compilation fails, or the program fails in run time (or returns wrong results). See the following example.

program arr
   implicit none
   integer :: a(2,2),b(2,2),c(2,2)

   a = reshape([1,2,3,4],shape(a))
   b = reshape([5,6,7,8],shape(b))

   ! matrix multiplication
   c = matmul(a,b)
   print *,c
end program arr

The function transpose gives you a transposed matrix.

program arr
   implicit none
   integer :: a(2,2),b(2,2),c(2,2)

   a = reshape([1,2,3,4],shape(a))
   b = reshape([5,6,7,8],shape(b))

   ! matrix multiplication
   c = matmul(a,transpose(b))
   print *,c
end program arr
Array functions

You can apply array functions to a particular dimension of an array using the second argument. For example, given a(2,2), you can compute the sum of elements by column (along rows) as sum(a,1). With this, the function returns a vector containing all results for rows (or columns).

! for exercise 3
program arr
   implicit none
   integer :: a(2,2)

   a = reshape([1,2,3,4],shape(a))

   print *,sum(a)
   print *,sum(a,1)
   print *,sum(a,2)
end program arr

Summary

Exercises

See the above code.

  1. What does b have? Why do we need b=0?
  2. What does c have? How do you compute the square root of each element in c?
  3. What will be shown?
  4. Write a program to show a matrix in row-wise (as in mathematical formula).

Back to index.html.