Exercises: Arrays
This is the first heavily code chapter, so most of these exercises want you at a keyboard: predict an output, then compile and check; port a snippet; hunt a bug; extend the solver. That predict-then-verify loop is the whole discipline — a result you computed in your head and then confirmed on the machine is a result you own.
Difficulty: ⭐ warm-up · ⭐⭐ standard · ⭐⭐⭐ deeper. Solutions: worked solutions to the daggered (†)
and odd-numbered problems are in appendices/answers-to-selected.md; the computational ones also appear as
compilable code in code/exercise-solutions.f90. Try every problem before you look. Compile everything with
gfortran -std=f2018 -Wall.
Part A — Declaration and Indexing ⭐
5.1 † For each declaration, state the rank, the shape, and the total size: (a) real(dp) :: v(10);
(b) integer :: g(5, 5); (c) real(dp) :: s(-3:3); (d) real(dp) :: cube(4, 4, 4).
5.2 What is the index of the first and the last element of real(dp) :: w(0:99), and how many
elements does it hold?
5.3 † Given integer :: a(4) = [10, 20, 30, 40], write down the values of a(1), a(4), size(a),
and sum(a). What would a(0) and a(5) do?
5.4 Write a do loop that fills real(dp) :: t(100) so that t(i) holds 0.1 * i (i.e. 0.1, 0.2, …,
10.0). Watch the types — why is 0.1 * i a trap, and what is the fix?
Part B — Sections and Whole-Array Operations ⭐⭐
5.5 † Let integer :: m(4,4) be filled with m(i,j) = 10*i + j. Write the array section for each, and
give its values: (a) row 3; (b) column 1; (c) the top-left 2×2 block; (d) every other element of row 2.
5.6 Given real(dp) :: a(5) = [1.0_dp, 2.0_dp, 3.0_dp, 4.0_dp, 5.0_dp], what does a contain after the
single statement a(2:4) = 0.0_dp?
5.7 † Predict the exact output, then compile to confirm:
real(dp) :: a(4) = [1.0_dp, 2.0_dp, 3.0_dp, 4.0_dp]
real(dp) :: b(4) = [4.0_dp, 3.0_dp, 2.0_dp, 1.0_dp]
print '(4f6.1)', a + b
print '(4f6.1)', a * b
print '(f6.1)', sum(a * b)
5.8 Rewrite the loop do i = 1, n; c(i) = a(i)*b(i) + 1.0_dp; end do as a single whole-array statement.
What does the array form tell the compiler that the loop form hides?
Part C — Intrinsic Functions (predict the output) ⭐⭐
5.9 † For integer :: v(6) = [5, 3, 8, 1, 9, 2], give the value of sum(v), product(v), maxval(v),
minval(v), maxloc(v, dim=1), and count(v > 4).
5.10 Let A = [[2, 0], [1, 3]] and B = [[1, 4], [5, 6]] (each row-by-row). Compute matmul(A, B) by
hand, showing all four entries.
5.11 † For the same A and B as 5.10, compute the elementwise product A * B. Why is it different
from matmul(A, B), and when would each be the right one to use?
5.12 Compute dot_product([2, 0, -1], [3, 4, 5]) by hand. Then state the shape of matmul(M, x) when
M is 3×3 and x has length 3.
Part D — Port It ⭐⭐
Translate the snippet to modern Fortran, and note where the array thinking differs.
5.13 † Port this NumPy to Fortran using a masked reduction (no explicit loop):
import numpy as np
r = np.array([-2.0, 4.0, 6.0, -1.0, 8.0, 2.0])
mean_pos = r[r > 0].mean() # mean of the positive entries only
5.14 In MATLAB, C = A*B is a matrix product and C = A.*B is elementwise. Write the Fortran for both,
given 3×3 real matrices A and B, and say which intrinsic or operator each maps to.
5.15 † Port this pure-Python loop to a single Fortran whole-array statement, and explain in one sentence why the Python version is slow while the Fortran version is fast:
y = [0.0] * n
for i in range(n):
y[i] = 2.0 * x[i] + 3.0
Part E — Find the Bug ⭐⭐
Each snippet is wrong. Say what happens (compile error or wrong/slow result) and fix it.
5.16 †
real(dp) :: a(3), b(4)
a = [1.0_dp, 2.0_dp, 3.0_dp]
b = a
5.17 A programmer coming from C writes, for real(dp) :: v(n):
do i = 0, n-1
v(i) = 0.0_dp
end do
5.18 † This compiles and gives the right answer, but a reviewer flags it. Why — and what is the fix?
real(dp) :: a(1000, 1000)
do i = 1, 1000 ! i = row
do j = 1, 1000 ! j = column
a(i, j) = 0.0_dp
end do
end do
5.19
real(dp), allocatable :: data(:)
data(1) = 3.14_dp ! before any allocate
Part F — Design It and Back of the Envelope ⭐⭐⭐
5.20 † (Design it — solver.) Extend the Chapter 5 project checkpoint. After computing the interior
Laplacian lap, add one statement that computes maxval(abs(lap(2:n-1, 2:n-1))) — the largest-magnitude
interior value, the kind of quantity a solver watches to decide it has converged. For the u(i,j) = i**3
field on the 4×4 grid, what value should it print?
5.21 (Design it — solver.) The plate's edges are held at fixed temperatures. Using array-section
assignments (no loops), write the four statements that set the top row of u(n, n) to 100.0_dp and the
other three edges to 0.0_dp. (Order matters at the corners — decide and justify who wins.)
5.22 † (Back of the envelope.) A simulation stores a temperature field of $1000 \times 1000 \times 100$ cells as `real(dp)` (8 bytes each). How many bytes is that? Express it in GiB ($1\ \text{GiB} = 1024^3$ bytes). Would it fit in a 16 GiB laptop? What about two such fields (current and next step)?
5.23 (Back of the envelope.) Multiplying two $n \times n$ matrices costs about $2n^3$ floating-point
operations. For $n = 1000$, how many operations is that? At a sustained $10^{10}$ operations per second, how
long does one such matmul take? (This is why §5.4 says matmul is fine for small matrices — and why
Chapter 21's LAPACK matters for large ones.)
Part G — Interleaved (Chapters 3 and 4) ⭐⭐
5.24 † (Ch. 3.) Predict the output and explain it: integer :: k(4) = [1, 2, 3, 4]; print *, sum(k) /
2. Then show the change that makes it a true (real) average.
5.25 (Ch. 4.) Rewrite this masked assignment as an equivalent do loop with an if. Which form do we
prefer in Fortran, and why?
where (a < 0.0_dp) a = 0.0_dp
5.26 † (Ch. 3 + 5.) Given integer :: counts(5) = [7, 4, 9, 2, 8], write a single expression that
computes their mean as a real(dp) value (not truncated). What is the numeric answer?
5.27 (Ch. 4.) Using a named do loop with exit, write a linear search that stops at the first
element of real(dp) :: v(n) that exceeds a threshold thr, recording its index. Then name the intrinsic
that does the same job in one call.
5.28 † (Synthesis.) Write a small program that: declares an allocatable real(dp) array, allocates it
to size m = 5, fills it with 1.0, 2.0, …, 5.0 via an implied-do constructor, prints sum and the mean,
and deallocates it. Give the expected output.
Solutions to the daggered and odd-numbered problems are in appendices/answers-to-selected.md; the
numeric ones are worked as runnable code in code/exercise-solutions.f90. Design problems 5.20–5.21 have a
model answer plus room for your own variations — as long as your sections are conformable and your output
checks out by hand, you are right.