Flow control

Yutaka Masuda

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 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.

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

Exercises

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.

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

  1. Confirm that the select statement can accept characters. Declare a character variable (say, character(len=10)::s), and make the branches by the variable (select case(s) or select 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 key to understanding the do loop is as follows.

  1. The do statement needs an integer variable (any name; i in this case) as a counter.
  2. The do statement has the following form: do variable = starting value, final value.
  3. Before the loop, the variable is initialized to the staring value.
  4. 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.
  5. Whenever the program reaches end do, the focus goes back to the corresponding do, 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.

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.

  1. Iteration: repeating the same (or similar) operations a finite number of times.
  2. 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

Exercises

  1. Rewrite the code to compute a factorial with a larger n using a double-precision variable.
  2. Read 10 numbers from the keyboard, then show how many positive numbers were given.
  3. Write a fizz buzz program. It prints integer numbers from 1 to n on screen, but when the number is divisible by 3 is replaced by the word fizz, any number divisible by 5 by the word buzz, and the numbers divisible by 15 by the word fizz buzz only (without showing any of fizz and buzz).

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

Exercises

  1. Print the following sequence of integer numbers on the screen (first 12 numbers of the sequence).
    1. \(2, 4, 6, 8, 10, 12, 14, \cdots\)
    2. \(0, 1, 3, 6, 10, 15, 21, \cdots\)
    3. \(1, -1, 1, -1, 1, -1, \cdots\)
    4. \(1, 2, 0, 4, 5, 0, 7, 8, 0, \cdots\)
    5. \(1, 2, 4, 5, 7, 8, \cdots\)
  2. Compute \(4x\) where \[x=1-\frac{1}{3}+\frac{1}{5}-\frac{1}{7}+\frac{1}{9}+\cdots\] for the first 1 million terms.
  3. 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

Exercises

  1. Show all pairs of integers (\(a\) and \(b\); \(0<a,b<20\)) to satisfy that \(a/b\) is an integer number.
  2. 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.