It is very easy to make a program repeat a sequence of instructions an indefinite number of times. However the example we are about to give is an example of very bad programming practice which should never be emulated! it is provided here simple to illustrate how easy concept is to implement.
Any instruction or sequence of instructions will be repeated indefinitely if it is preceded and terminated respectively by the two instructions shown below:
do ! instructions to be repeated here end doIt is considered good practice to indent every instruction between the do and end do by three character spaces.
Here is a very simple program which illustrates this. It is a very bad program, because as written it will never stop running!
program zap ! This is not a good program !!! ! Before running it, make sure that you can identify ! `panic button' which stops programs on you computer. ! On Unix systems it is simultaneously `Control' and `c' !JWP 29/10/98 implicit none do print *, 'ZAP!!' end do end program zap
If you are going to compile and run it, first make sure you know how to do an `emergency stop' on your computer system. This is usually achieved by pressing simultaneously the letter `c' key and the key marked `Control' or `Cntrl'.
The principle should however be clear from this example....
To be useful, the repetition clearly needs to be under rather better control! A useful way of achieving this is to count the number of repetitions and stop after a predetermined number. the means of doing this is described next.
do times = 1, 10 print *, 'ZAP!!' end doThe only difference between this and the previous example (so far) is in the first line. The interpretation of this can be consider to be as follows:
Like any other variable, times must be declared at the beginning of the program, before it is used. However, because times will have values which are always whole numbers or integers, the form of the declaration is different from those seen so far:
integer :: timesHere is the complete, and much improved, program:
program zap ! This is a better program !!! ! JWP 29/10/98 implicit none integer :: times do times = 1, 10 print *, 'ZAP!!' end do end program zapCopy it from this link.
The counter variable has other uses than just stopping this program loop. Its value is 1 the first time round, 2 the second and so on.
Try changing the instruction in the loop to:
print *, 'ZAP times ..', timesand confirm this.
There are a number of other uses for integer variables, but a major one is controlling loops like this. The number of times the loop is to be repeated, and, associated with it, the starting and finishing values of the counter variable, must be specified before the do instruction is encountered, but may in fact be general integer expressions.
Note that in this example the numbers `1' and `10' were written without decimal points, whereas these were required for numbers in the general expressions encountered previously. This is because these starting and finishing values must be whole numbers. The following would also be allowed, try them, printing out the counter value on each loop:
The following sections provide programs to illustrate the use of repetition loops and a number of other programming techniques. You are encouraged to copy and modify these as described.
program squares ! calculate squares ! JWP 29/10/98 implicit none integer :: number, square integer, parameter :: max = 10 ! print *, 'Squares of numbers from 1 to', max ! do number=1, max square = number ** 2 print *, 'Square of ', number, ' is ', square end do ! end program squares
Modify it to calculate and print the squares of numbers from 1 to 15 by altering only a single character in the program.
program squares ! calculate squares ! JWP 29/10/98 implicit none integer :: number integer, parameter :: max = 10 real :: real_number, root ! print *, 'Table of square roots' print *, 'Number ', ' Square root' ! do number=1, max real_number = real(number) root = sqrt(real_number) print *, number, root end do ! end program squares
Square roots can also be calculated by raising numbers to the power 0.5, or in Fortran writing ** 0.5 Check that this gives the same answers, and modify the program to tabulate cube roots.
integer :: i real :: x ..... do i = 1, 12 x = i * 0.25 print *, x, sqrt(x) end do ...There is a more general way of doing this described later.
x = x + 3If you are in any doubt about what will happen, complete the following program segment by adding the necessary additional instructions, and run it.
..... x = 4 print *, 'x was', x x = x + 3 print *, 'x now is', x .....There are many circumstance where it is convenient or necessary to use this kind of `recursive' instruction. One such is illustrated in the example below. Complete this program and use it to compute some factorials. Check to see that it gives the correct answers! 5! is 120, 10! is 3628800. What happens when you try to calculate 100! which is about 10158?
n! = 1 . 2 . 3 .... (n-1) . n
Consider the following program segment:
... integer :: fact, n, i n = 5 fact = 1 do i=2,n fact = fact * n end do ...
Recall that this quantity is equal to ½(n+1)n.
real :: Tprop, Tbut, T, dT integer :: i, n .... n=10 ! number of points ... dT = (Tbut - Tprop) / (n-1) ! divide range by number of INTERVALS ! .. this gives size of interval T = Tprop ! Start here do i=1, n print *, T ! the current temperature T = T + dT ! next temperature ... end do ...Exercise: Write a program to tabulate the vapour pressures of propane and butane at 12 equispaced temperature points between their respective 1atm boiling points.
Consider the following sequence of instructions:
.... if ( a >=0.0 ) then print *, 'a is zero or positive' else print *, 'a is negative' end if ...Their implication should be fairly obvious. If the variable a has a value greater than or equal to zero, then the instruction:
print *, 'a is zero or positive'is carried out and the appropriate message printed. On the other hand if this is not the case (`else') then the instruction:
print *, 'a is negative'is obeyed.
The program would then proceed with any instructions following end if.
The above is an example of the most general form of the conditional or `if' construction used for decision making in computer programs. The lines between then and else and between else and end if may contain any sequence of legal `executable' instructions. (This excludes `formal' instructions like real :: or end program.) As with do loops, note the indentation of the instructions by three character spaces. This is to give clarity to the program.
As an example of using this for of the `if' construction, here is a program to solve a quadratic equation using the quadratic formula. The program tests to see if the equation has real roots before proceeding with the solution.
program quadratic ! solve a quadratic with real roots ! JWP 22/10/98 implicit none real :: a, b, c, d, x, x1, x2 ! a = 1 b = 2 c = -3 ! print *, 'Solving quadratic:' print *, a, ' x**2 +', b, ' x + ', c ! check nature of roots.. ! d = b**2 - 4.0*a*c ! if (d<0) then print *, 'Has complex roots.' else x1 = (-b + sqrt(d)) / 2.0 / a x2 = (-b - sqrt(d)) / 2.0 / a print *, 'Roots are:', x1, x2 end if ! end program quadratic
Three other forms of the `if' construction are useful. Sometimes it is only required to carry out a series of instructions if a condition is satisfied. This is achieved by the following. Note that the symbols /= are the Fortran version of the `not equals' sign.
if (a /=0.0 ) then print *, 'a is not zero so can be a divisor' x = 1 / a end ifNotice that in this case nothing happens if the condition is not satisfied, i.e. if a is equal to zero. In this case the value of x is not set by any instruction shown in this program segment.
If only a single instruction is to depend on the outcome of the condition then it may be shortened to:
if (a /= 0.0 ) x = 1 / aFinally, by using else if several conditional statements may be chained together:
if(a > 0) then print *, 'a is bigger than 0' else if (a < 0) then print *, 'a is less than 0' else print *, 'a must equal zero' endif
`<' and `>' have their obvious forms, <= stands for `less than or equal to' and hence the obvious interpretation of >=.
'Not equal to as above is /=.
Confusingly `equal to' must be written with two equals signs, i.e. as ==.
program random ! generate some random numbers ! JWP 22/10/98 implicit none real :: x real, external :: rand integer :: i do i= 1, 10 x = rand(0) print *, x end do end program random(Note the special form of declaration: real, external :: rand which says that rand, a function to generate random real numbers in the range 0.0 to 1.0, is to be found outside, i.e. `external' to the program. This is one of a number of useful function which exist in special libraries.)
Copy the program from here and modify it (a) to print only random number greater than 0.5, and (b) to count and subsequently print out the number of such numbers.
exitwhich can only appear inside a do .. end do loop causes a immediate termination of the loop. The program carries on at the instruction following end do.
The do may be either with or without a counter variable. In the latter case the count is overridden and the loop stops at whatever is its current value.
The exit instruction is normally always the subject of a condition, e.g.:
if (TK <= 0.0) exit
There is another method of stopping a do...end do loop using a conditional statement:
do while (x == 1) . . . end doThis is equivalent to saying:
do if(x /= 1) exit . . . end doThe do while format is preferred by purists as the loop can only terminate after a complete iteration.
This section has been left until last because we do not particularly want to encourage you to write `interactive' programs until you are reasonably experienced in writing self contained programs.
All the programs shown to you so far have been `self contained' in that all the information required for the program to work is contained within the program.
All your handin examples should normally be written in this way. A major advantage of such a program is that it's operation can be checked by reference only to a printout of the program. A disadvantage is that every time we want to change a parameter then the the program file must be edited and recompiled.
We will now discuss how to make programs more `user friendly' and `interactive'.
When ever the computer encounters an instruction like the following:
read *, xit does two things.
First it stops and waits for the user to type something on the keyboard. Secondly it interprets whatever has been typed in the context of what kind of variable x has been declared to be, and if it can, assigns whatever has been typed in to x. This should be clear from the following complete program which you might like to copy and try:
program readin real :: x print *, 'Type in a valid real number:' read *, x print *, 'The number you typed was ', x end programYou can copy the program from here. If you are trying it out, see what happens if you type either an integer or something that isn't a valid number.
Note that the variable which appears in the read instruction must be a variable which is not declared as a parameter.
It is possible in principle to have more than one variable in the list following read *, but this is not a good idea when numbers are being read from the keyboard. (They can come from elsewhere, as will be discussed later.) Also you should always print out a message to remind the user what to type in.
We have already encountered an example of a `procedure call' in the print instruction. we can (loosely) define a procedure call as an instruction which `causes something to happen' other than, or in addition to, simply assigning a value to a variable.
Procedure calls have two main characteristics. Firstly the procedure invoked by the call has a name, e.g. print. Secondly there are usually one or more arguments which specify with what the procedure is to perform whatever it is meant to do.
The name of the procedure is fixed, but the programmer chooses the arguments. Thus:
print *, 'Hello'has as an argument the character string 'Hello' and so that is what gets printed.
print *, whateverhas the variable whatever as an argument, so its value gets displayed.
print is a rather special procedure which is built into the language. There are some others like it which you will encounter later. Another type of procedure is the subroutine procedure or simply a subroutine. These are not built into the language but are available in special libraries or can be defined by the programmer.
At this point we will introduce one example of a subroutine procedure which is available in a standard library, and another user defined subroutine which will be supplied to you. Writing your own subroutines will be covered later.
A subroutine procedure call which activates the procedure consists of the word call followed by the name of the procedure and then a list of its arguments (if any) in brackets.
system takes a single argument, the name of the Unix command as a character string in quotes, e.g. 'ls'
The following complete program will cause a listing of your files to be printed on the screen when the compiled program is run:
program list_files
! An minimum example of a subroutine procedure call
! JWP 22/10/98
implicit none
! This instruction makes it work:
call system ('ls')
!
end program files
A version of this program
can be copied from
here. You can try changing
`ls' to `ls -l' and see what happens.
The above versions of the program are the minimum size and complexity required to access this subroutine. If you learn more about the Fortran 90 language you will discover that a stricter and more pedantic form is available. For completeness we have supplied a `long' version of the program here.
subroutine atomic (element, number, value)
! look up atomic no. and weight of some common elements
! JWP 22/10/98
implicit none
character *(*), intent (in) :: element
real, intent (out) :: value
integer, intent(out) :: number
integer, parameter :: na=12
integer, dimension(1:na), parameter :: an = &
(/17, 35, 11, 20, 6, 1, 7, 8, 16, 9, 53, 19/)
real, dimension(1:na), parameter :: aw = &
(/35.5, 79.9, 22.0 , 40.1, 12.0 , 1.0, 14.0, 16.0, 32.1, 19.0, 126.9, 39.1/)
character*48, parameter :: ename = &
'Cl,Br,Na,Ca,C ,H ,N , O ,S ,F ,I ,K '
integer :: i
character *(2) :: copy
!
copy = adjustl(element) // ' ' ; copy = copy(1:2) ! fiddle with length etc.
i = index (ename,copy) ! look up element in list
if (i==0) then
print *, 'I do not have data for element ' // element
value = 0.0 ; number = 0
return
end if
!
i = (i-1)/3 + 1 ! Table entry number
value = aw (i)
number = an (i)
end subroutine atomic
To make use of this subroutine you must do two things. Firstly, as in the previous example, you must write a program to `call' the subroutine. A typical call might look like this:
call atomic ('K', Kno, Kwt)
Here the string 'K' determines that it is the
properties of potassium which are required. Kno
is a variable which you the programmer must have declared
as an integer in your program. The subroutine
call will set this to the atomic number of potassium.
Kwt must have been declared as a real
variable and will contain the corresponding atomic weight.
Note that these can be any appropriately declared variables and can
thus have any names that you want to give them.
To use use this subroutine you also need to let your program know about it. The simplest way to to this is to copy the whole subroutine from the link and put it in the same file as the program you are writing which will use the subroutine. the layout of the file will then be as shown below.
program atom_stuff
! My own program, declarations etc. go here..
real :: Kwt, ...
....
call atomic ('K', Kno, Kwt)
print *, 'Properties of Potassium..', ...
...
end program atom_stuff
! What follows is just copied..
subroutine atomic (element, number, value)
! look up atomic no. and weight of some common elements
... etc
...
end subroutine atomic
It is not necessary that you understand how atomic
actually works, but it is useful to note the existence of
variables which will hold character strings. a declaration
such as:
character *(2) :: elementdeclares a variable called element for storing not numbers but letters, up to a maximum length of two characters.
The intent attribute informs a programmer using a subroutine whether a specific argument is to be:
The structure of a subroutine is quite similar to that of a program:
function calc(x) !this function evaluates a simple formula !Ross Andrews 26/10/99 implicit none real, intent(in) :: x real :: calc calc = x**2 + 3*x - 4 end function calcThe main point to note here is that the function name, calc is declared as a variable, and assigned to, in the function. This will be the value returned to the calling program. In the calling program, the function calc must be declared as follows:
real, external :: calci.e. external to the main program, and can be used as:
y = calc(43.2)
Fortran has a number of built-in functions, some of which you have used already:
The variables in the subroutines and functions (subprograms) described here are independent from the main program variables, and vice versa. What this means in practice is that subprograms cannot refer to main program variables, unless they are passed as an argument, and likewise in the main program.
There may be some confusion as to whether a subroutine or a function best suits a particular task. Whilst this is down to the preferences of the individual programmer, there are a number of guidelines: