Flow control
February 2020
Back to index.html.
Flow control
As previously noted, a Fortran program sequentially executes the code from the beginning through the end. By default, the program does not skip any single statement and does not go back to the previous statements. We are going to see some control statements which change the flow or repeat the same statements many times.
Conditional
The if statement
Simple example
You often want to change the flow by the condition. A typical example is to test the input value; you can print an error message if the values are invalid. To make branches by condition, Fortran provides the if statement, as seen in the following example.
program cond
implicit none
real :: x
read *,x
if(x<0.0) then
print *,'The value should be 0 or positive.'
stop
else
print *,'square root=',sqrt(x)
end if
end program cond
Note that I put an indent to the if statement to make the structure clearer (and it is a good practice). The stop keyword stops the program immediately. You can see how to use the if statement with this example.
- The
ifstatement starts withif (condition) thenand ends withend if. The condition is a logical expression which returns.true.or.false.. - The
ifstatement has 2 blocks: the first one betweenifandelse, and the second one betweenelseandend if. Each block is exclusively executed by the condition in theifstatement. - If the condition is true, the program proceeds the first block and exits the statement (jumps to the next line of
end if). - If the condition is false, the program jumps to the second block and exit the statement.
The above if statement has the following structure.
if(condition 1) then
{block}
else
{the other block}
end if
The if statement has some flexible structure. The following code runs in the same way as above (only the kernel of the program is shown).
! Alternative 1
if(x>=0.0) then
print *,'square root=',sqrt(x)
else
print *,'The value should be 0 or positive.'
stop
end if
! ----------------------------------------------
! Alternative 2
if(x<0.0) then
print *,'The value should be 0 or positive.'
stop
end if
print *,'square root=',sqrt(x)
The first alternative has the opposite condition as the original one. The second alternative has no else block; the program stops if the conditional meets, so it does not need the else block.
General form of if
The if statement has the following general form.
if(condition 1) then
{block 1}
else if(condition 2) then
{block 2}
else if(condition 3) then
{block 3}
...
else
{the other block}
end if
In the above case, the program can be interpreted as follows.
- The program first tests Condition 1, and if true, it executes Block 1 only and exits the statement.
- If Condition 1 is not true, then it tests condition 2, and if true, it executes Block 2 and exits the statement.
- If Condition 2 is not true, then it tests condition 3, and if true, it executes Block 3 and exits the statement.
- You can put more
else ifstatements between the firstifandelse. - You can optionally put
elseat the bottom of the statement. The Else block will be executed if all the conditions do not meet.
When there is no else block, if all conditions do not meet, the program does nothing and exits the statement.
By the way, Fortran accepts endif instead of end if (and elseif vs. else if). Also, spaces are allowed to be between if and ( i.e. if (condition) then. This tutorial uses end if and else if, but it is my preference. You can use your favorite style.
One-line if
When you have only 1 block, i.e. if(...) then ... end if and the block has only 1 statement, you can write the code in 1 line.
program cond
implicit none
real :: val
read *,val
if(val==0.0) stop
print *,val
end program cond
Logical expressions
You can combine several conditions with .and. and .or.. Recall the rule to make logical expressions. The following program precisely finds the error of input values and gives the user messages about what is wrong. It accepts two positive numbers from the keyboard and calculates a value.
program cond
implicit none
real :: x,y
read *,x,y
if(x<0 .and. y<=0) then
print *,'error: x<0 and y<=0'
else if(x<0) then
print *,'error: x<0'
else if(y<=0) then
print *,'error: y<=0'
else
print *,'sqrt(x)*log(y)=',sqrt(x)*log(y)
end if
print *,'done.'
end program cond
Nested if statements
The if statements can be nested, which means you can put it in another if statement. You can cascade a conditional test to another. The following example is an alternative version of the previous program.
program cond
implicit none
real :: x,y
read *,x,y
if(x<0) then
print *,'error: x<0'
else
if(y<=0) then
print *,'error: y<=0'
else
print *,'sqrt(x)*log(y)=',sqrt(x)*log(y)
end if
end if
print *,'done.'
end program cond
In this case, the program does not detect the case where both x and y are invalid at the same time. The original code looks flat and checks all possible cases on the same line. The above code shows a clear intent that checks x first and y second.
I have to admit that the above example is artificial to show the nested-if. The code is equivalent to the following program with a single if-elseif-else block.
if(x<0) then
print *,'error: x<0'
else if(y<=0) then
print *,'error: y<=0'
else
print *,'sqrt(x)*log(y)=',sqrt(x)*log(y)
end if
Summary
- The
ifstatement starts withif(condition) thenand ends withend if. - The condition has a logical expression resulting in a single
.true.or.false.. - You can optionally insert
else if(condition)as many as possible and oneelseinto the statement. - If the condition meets, the program executes the immediately beneath block and exits the current
ifstatement (moves to the next line ofend if). - If the condition does not meet, the program jumps to the next
else ifand evaluates the condition and decide whether the program takes the block or not. - If all the conditions are false and there is the
elseblock, the program executes it. - The
ifstatement can be nested.
Exercises
- Read 1 integer (
code) and 1 real numbers (v) from the keyboard. If the code is1, compute the square root ofv. If the code is2, compute the sine ofv. If the code is the other values, print an error message and stop the program. - Read an integer and show if it is an even or odd number.
The select statement
Simple example
There is a convenient statement (select) to make branches conditioned by a single value. The select statement is useful to compare between a scalar with a literal (or, a range of literals). Here is a piece of code that shows a typical case where select works well.
integer :: x
if(x==1) then
print *,'case 1'
else if(x==2 .or. x==3) then
print *,'case 2 or 3'
else
print *,'failed'
end if
With the select statement, the code becomes readable and straightforward as follows.
integer :: x
select case(x)
case(1)
print *,'case 1'
case(2,3)
print *,'case 2 or 3'
case default
print *,'failed'
end select
The select statement has the following structure.
- The statement starts with
select case(x)andend selectwherexis a single variable or a literal (i.e., scalar). - The statement has 1 or more
case(y)whereyis a literal, a range, or a list of them.yshould not be a variable. For example,case(1),case(1,2,5),case(2:3),case(2:3,5)are all valid. - The program starts comparing
xwithyfrom the firstcaseblock to the last. Ifxequals toy, the program performs this block, and exit the entireselectstatement without moving toward the next block. - If
xdoes not match anyy, the program executes the optionalcase defaultblock. If you omit this block, the program does not do anything.
Here is a general form of the select statement.
select case(scalar)
case(literal list 1)
{block 1}
case(literal list 2)
{block 2}
...
case default
{the other block}
end select
Exercises
- Confirm that the
selectstatement can accept characters. Declare a character variable (say,character(len=10)::s), and make the branches by the variable (select case(s)orselect case(s(1:2))).
Iteration
The do statement
Small example
There is a case where you have to repeat the same operation many times. Or, you need to repeat slightly different but almost the same operation by round. The simplest way is to write all the repeated operations as a flat program, but it should be in trouble in its readability and redundancy. Fortran provides a systematic way to repeat the same (or similar) operation with the do statement.
For example, consider printing Hello, world! 3 times. It can be written in Fortran as follows.
program loop
implicit none
integer :: i
do i=1,3
print *,"Hello, world!"
end do
end program loop
Hello, world!
Hello, world!
Hello, world!
You may need some explanation.
- The operations (block) that you want to repeat are placed between
doandend do. - The
dokeyword accompanies with an integer variable, here,i. - The variable
iis automatically initialized to1(as specified in thedostatement) before the loop starts. - In the 1st iteration,
iis compared with the last value3asi<=3. It is true, and the program executes the block.- When the program reaches
end do, it jumps back todo, and incrementiby 1. In this case,ibecomes 2.
- When the program reaches
- In the 2nd iteration,
iis compared with the last value3asi<=3. It is true, and the program executes the block.- When the program reaches
end do, it jumps back todo, and incrementiby 1. In this case,ibecomes 3.
- When the program reaches
- In the 3rd iteration,
iis compared with the last value3asi<=3. It is true, and the program executes the block.- When the program reaches
end do, it jumps back todo, and incrementiby 1. In this case,ibecomes 4.
- When the program reaches
- In the 4th iteration,
iis compared with the last value3asi<=3. It is false, and the program jumps out ofend do.
The key to understanding the do loop is as follows.
- The
dostatement needs an integer variable (any name;iin this case) as a counter. - The
dostatement has the following form:do variable = starting value, final value. - Before the loop, the variable is initialized to the staring value.
- Whenever the program reaches
do, the variable is compared to the final value. If the variable is smaller than or equals to the final value, the focus moves to the next line; Otherwise, the program exits the current loop. - Whenever the program reaches
end do, the focus goes back to the correspondingdo, then the variable is incremented by 1 (and going back to Step 5).
Here is a general form of do.
do counter_variable = initial_value, last_value
{block}
end do
In this usage, do needs a counter variable. This behavior is like to count up numbers by hand. The counter variable is checked only at the beginning of loops, and it is incremented when going back from end do to do.
By the way, this program also works when i=1,3 is replaced with i=0,2 or even i=-5,-3 (please consider why). Non-1 starting value looks unusual but can be useful in future use.
Use of the counter variable
The counter is an integer variable, so the program refers to the value of this variable in each iteration (but can not rewrite the variable because the statement protects it). In the above case (i=1:3), the counter i is 1 in the 1st iteration, 2 in the 2nd, and 3 in the last iteration. After the loop, i becomes 4.
The previous example can be slightly changed to show the current iteration number.
program loop
implicit none
integer :: i
do i=1,3
print *,"iteration",i,": Hello, world!"
end do
end program loop
iteration 1: Hello, world!
iteration 2: Hello, world!
iteration 3: Hello, world!
This variable can also be used in a numerical formula. The following example computes the multiple of 3.
program loop
implicit none
integer :: i
do i=1,9
print *,i," x 3 = ",i*3
end do
end program loop
1 x 3 = 3
2 x 3 = 6
3 x 3 = 9
4 x 3 = 12
5 x 3 = 15
6 x 3 = 18
7 x 3 = 21
8 x 3 = 24
9 x 3 = 27
Any other variables can be used inside the loop. The following program calculates the factorial of an arbitrary integer read from the keyboard.
program loop
implicit none
integer :: i,n,fact
read *,n
fact = 1
do i=1,n
fact = fact*i
end do
print *,n,"! = ",fact
end program loop
# when putting 5:
5 ! = 120
Note that the variable fact for storing the result must be initialized by 1 before the loop. This program does not work if you put a large number (it overflows). The variable is changed to double precision to fix this issue (see exercises).
The counter variable should be an integer. If you need a real number that incrementally changes, you have to calculate the value from the counter. The following program calculates the square root of a real number ranged from 0 to 1 by 0.1.
program loop
implicit none
integer :: i
real :: v
do i=0,10
v = real(i)/10
print *,sqrt(v)
end do
end program loop
Break and skip the loop
You may break (exit) a loop when a condition is true. The keyword exit immediately exits the do statement by jumping to the next line of end do. It may happen when you repeatedly read a value from the keyboard and stop reading when the input is invalid.
program loop
implicit none
integer :: i
real :: val
! read value 10 times but exit the loop if the input is negative.
do i=1,10
read *,val
if(val < 0) exit
print *,sqrt(val)
end do
end program loop
If you want to skip the rest of the statements in the loop and start the next iteration, you can use the cycle keyword, which immediately moves the focus to the beginning of do. In the above code, if you replace exit by cycle, it jumps back to the do statement and starts the next round. You should see if this program reads the value precisely 10 times.
program loop
implicit none
integer :: i
real :: val
! read value 10 times but skip the round if the input is negative.
do i=1,10
read *,val
if(val < 0) cycle
print *,sqrt(val)
end do
end program loop
Infinite loops
If you don’t know how many iterations are needed, do continues the loop forever. Such a structure is called infinite loops, and it never ends unless the program meets exit in the loop. Making infinite loops, you just drop the iterator and use do alone.
program loop
implicit none
real :: val
! read a value but exit the loop if the input is negative.
do
read *,val
if(val < 0) exit
print *,sqrt(val)
end do
end program loop
Here is a general from of the infinite loops. The code block should have the exit statement.
do
{block with exit}
end do
Again, this unconditional do statement creates the infinite loops, and the program may never stop unless the program reaches exit. The compiler can not detect the infinite loops as a failure. If you run into this issue, hit the Control key (Ctrl) and the C key simultaneously (hit C while pushing Ctrl).
Writing a loop
The loop in a programming language has a unique syntax that is not necessarily clear to the beginners. If you know that you have to write a loop but do not know how to write it, please consider the following method.
First, you can think of the result of the loops. For example, you want to get the following output on the screen.
number = 3
number = 4
number = 5
Then, you look for a common thing across iterations.
- Each output should be from the
printstatement, and you can find the common message in the output,number =. - The common statement over the loops is
print *,"number =". - You can put a variable to stuff that changes by iteration:
print *,"number = ",x.
Finally, you enclose the common code by the do statement.
do
print *,"number = ",x`
end do
If the variable systematically changes by iteration, it can be a counter variable. In this case, x ranges from 3 to 5, and here is the code.
do x=3,5
print *,"number = ",x`
end do
Or, you may think of the following code.
x = 3
do
print *,"number = ",x`
x = x + 1
if(x>=6) exit
end do
The first loop is more transparent for any people than the latter, while the latter code is very straightforward (but it may look more complicated for some people). Although the first code is preferred, we would need a loop like the other code.
There are many cases to generate a sequence in Fortran. The do statement is a typical mechanism to generate the sequence, and it is a solution for the purpose.
Practical use of loops
The loop has 2 different viewpoints in practice.
- Iteration: repeating the same (or similar) operations a finite number of times.
- Sequence of numbers: generating a series of numbers.
The latter is useful when you manipulate the subscript (index) of an array (e.g., matrix and vector that we are going to see later). When you have to change the index or any numbers systematically, you will use the loops.
Summary
- The
dostatement defines a loop. The statements betweendoand the correspondingend doare repeated. - The loop can have an integer variable indicating how many times the loop is repeated. The user can refer to the counter as a variable.
- When the program reaches
end do, the focus goes back to the correspondingdo, then the counter is incremented by 1. - The evaluation of the counter occurs at
do, the top of the loop. If the counter is greater than the final value, the program exits the loop, i.e., going to the next line ofend do. - The
exitstatement jumps out of the loop. Thecyclestatement immediately goes back todo, ignoring the remaining statements. - Without the counter,
domakes an infinite loop. Theexitstatement is the only way to exit the loop. - The loop is useful to repeat a task many times or to generate a sequence of numbers.
Exercises
- Rewrite the code to compute a factorial with a larger
nusing a double-precision variable. - Read 10 numbers from the keyboard, then show how many positive numbers were given.
- Write a fizz buzz program. It prints integer numbers from 1 to
non screen, but when the number is divisible by 3 is replaced by the wordfizz, any number divisible by 5 by the wordbuzz, and the numbers divisible by 15 by the wordfizz buzzonly (without showing any offizzandbuzz).
Variation in loop
In this subsection, we are going to see a variation of the loop, particularly useful to generate a sequence of numbers.
Non-executable loop
You can use variables as the staring and the last values. In such a case, the loop may not be executed. It is a valid program, and it often appears in some algorithms.
program loop
implicit none
integer :: i,a,b
read *,a,b
! not executed when a>b
do i=a,b
print *,i
end do
end program loop
Non-1 increment
By default, the counter variable is incremented by 1. The user can change the increment to any non-zero integer as the 3rd parameter in the do statement. For example, the following code shows odd numbers up to 10 (actually 9).
program loop
implicit none
integer :: i
! counter incremented by 2
do i=1,10,2
print *,i
end do
end program loop
1
3
5
7
9
It is equivalent to write the following code. You can use whatever you prefer.
do i=1,5
print *,2*i-1
end do
Using this technique, the user can define a loop where the counter decrements.
program loop
implicit none
integer :: i
! from 5 to 1 by -1
do i=5,1,-1
print *,i
end do
end program loop
5
4
3
2
1
Summary
- If the staring value is already larger than the final value in a loop, such a loop will not be executed.
- The increment is defined in the 3rd parameter in
do. It should be non zero integers.
Exercises
- Print the following sequence of integer numbers on the screen (first 12 numbers of the sequence).
- \(2, 4, 6, 8, 10, 12, 14, \cdots\)
- \(0, 1, 3, 6, 10, 15, 21, \cdots\)
- \(1, -1, 1, -1, 1, -1, \cdots\)
- \(1, 2, 0, 4, 5, 0, 7, 8, 0, \cdots\)
- \(1, 2, 4, 5, 7, 8, \cdots\)
- Compute \(4x\) where \[x=1-\frac{1}{3}+\frac{1}{5}-\frac{1}{7}+\frac{1}{9}+\cdots\] for the first 1 million terms.
- Factor \(2099829353\) into two primes (i.e. find primes \(x\) and \(y\) so that \(xy=2099829353\)).
Nested loops
You can define nested loops with multiple dos. Here, for simplicity, we just consider double loops with two do statements.
Double loops
As the if statement, do can also be nested. You can see how the nested loops work in the following example.
program loop
implicit none
integer :: i,j
do i=1,3
do j=7,9
print *,i," x ",j,"=",i*j
end do
end do
end program loop
1 x 7 = 7
1 x 8 = 8
1 x 9 = 9
2 x 7 = 14
2 x 8 = 16
2 x 9 = 18
3 x 7 = 21
3 x 8 = 24
3 x 9 = 27
This program prints some products. When the program enters the first loop do i=1,3, the initial i is set. Then the program enters the second loop do j=7,8. In the second loop, i is preserved, and j is updated in this loop. When the second loop ends, the focus returns to the top of the first loop, then i is incremented, and the second loop is executed again. When The outmost loop ends, the entire loop block is done.
exit and cycle
With multiple loops, exit and cycle are valid only for the current loop. For example, the following code tries to exit the loop when \(i\times j = 16\).
program loop
implicit none
integer :: i,j
do i=1,3
do j=7,9
if(i*j==16) exit
print *,i," x ",j,"=",i*j
end do
end do
end program loop
The exit statement is active when i=2 and j=8, and it just exits the current (the innermost) loop. So, the output looks like this.
1 x 7 = 7
1 x 8 = 8
1 x 9 = 9
2 x 7 = 14
3 x 7 = 21
3 x 8 = 24
3 x 9 = 27
It surely skips i=2 and j=8. But what if you want to exit the entire loop (the outer loop)? The simplest option is stop, but it stops everything. In Fortran, you can specify do, which you particularly exit by using a label. A label is an arbitrary name stuck to do, and the corresponding end do statements. In the following example, the outer do has a label outer with a colon (:), the corresponding end do has the same label, and exit specifies the label.
program loop
implicit none
integer :: i,j
outer: do i=1,3
do j=7,9
if(i*j==16) exit outer
print *,i," x ",j,"=",i*j
end do
end do outer
end program loop
1 x 7 = 7
1 x 8 = 8
1 x 9 = 9
2 x 7 = 14
The same principle applies to cycle.
Summary
- Nested loops can be defined.
- The
do(andend do) statement can have a label, which is a sequence of arbitrary characters. - The label is used by
exitandcycleto specify the loop.
Exercises
- Show all pairs of integers (\(a\) and \(b\); \(0<a,b<20\)) to satisfy that \(a/b\) is an integer number.
- Write a program to find triplets of integer numbers (\(a\), \(b\), and \(c\)) to satisfy \(a^2+b^2=c^2\) where \(0<a,b,c<1000\). Just show the first 10 sets of numbers.
Back to index.html.