A part of the program that conceptually belongs together, or that will be used more than once, possibly with minor variations, can be given a name, and be invoked by that name. There are two kinds of subprograms: functions are the subprograms that calculate one value; subroutines are the subprograms that compute multiple values, or make changes to program data.
Subprograms can be placed in separate files, or they can be in the same file as a main program:
PROGRAM prog
...
END
SUBROUTINE subr
...
END
FUNCTION fun
...
END
The order in which main program and subprograms are placed is completely irrelevant. In particular, unlike in such languages as Pascal or C, subprograms can be used before they are defined. (In C terminology, not even prototypes are needed.)
The introduction of functions is one way to give a name to a part of the computation that mostly serves to compute a single value.
User defined functions can be used in any expression, in the same way as library functions are:
x = sin(3.57)+myfun(4.023)
This line, or more generally the (sub)program it appears in, is called the calling site of the function. There is also the defining site of the function: its actual definition.
Often, it is not necessary to declare the type of library functions, but user-defined functions need to be declared: eg,
REAL myfun
This function is defined somewhere in the source as a separate subprogram:
FUNCTION myfun(x)
REAL myfun,x
...
myfun = <some expression using x>
RETURN
END
This definition can be in a different file. As a result, the compiler will not complain processing the use of the function if you forget actually to define the function. Only when you link your program will you get an error message about a missing function.
A function can have zero or more arguments. For instance, a function
named random will likely have no arguments. Even if there are
no arguments, the parentheses are mandatory at both calling
and defining site. Example:
FUNCTION random()
random = <some expression>
RETURN
END
....
x = random()
If a function has arguments, these must be declared in the function definition, just as any local variables.
At the defining site of the function its type needs to be declared. This can be in two ways: you can write
type FUNCTION name (args)
or you can write
FUNCTION name (args)
and let the name be declared implicitly
or explicitly:
type name
If you use the first form, you cannot have the name also appear in an explicit type statement.
The value computed in a FUNCTION subprogram is passed back
to the calling site as follows: it is assigned to the function name. Example:
FUNCTION comput(val)
comput = <some expression using val>
RETURN
END
The assignation can happen in other ways, for instance by passing the function name as argument to a subroutine
CALL comput2(comput)
that assigns a value to its argument. One could even write
FUNCTION iwhere(arg,arr,len)
DIMENSION arr(len)
DO 10 iwhere=1,len
IF (arr(iwhere).EQ.arg) RETURN
10 CONTINUE
END
In addition to the normal result assignment, a function can also return results by changing the value of its arguments. While this is the accepted way for subroutines to pass back information, for functions this is called a side-effect, and it is not a recommended programming practice. If you do use side effects, make sure they don't alter variables used in the same expression as the function call: in
X = Y + F(Y)
it would be a bad idea if F altered its argument, since
this would make the value of the right-hand-side expression dependent on
the evaluation order, which is not prescribed in the language standard.
For a computation that can be expressed in a single statement, and that occurs several times in one program unit, a statement function can be defined. Example:
AVG(X,Y) = (X+Y)/2.0
This function declaration has to come after any other non-executable statement, and before the first executable statement. Now you can use the function so defined as part of any expression in this subprogram:
Z = AVG(3.0,2*X) + 5.1
In addition to the function definition, you need to declare
the type of the function name and its arguments in the function definition.
You can not declare a statement function as EXTERNAL.
Subroutines look a lot like functions, but they differ in a few ways. Instead of delivering a single value that can be used in an expression at the calling site, they take some data of the calling site and process or alter it in some way.
SUBROUTINE out(x)
PRINT *,'The final result is ',x
RETURN
END
the call
CALL out(3.7)
will give the same result as writing
PRINT *,'The final result is ',3.7
Another example: if a subroutine is invoked by
CALL subr(x,y)
and its definition is:
SUBROUTINE subr(a1,a2)
a1 = 1.
a2 = 3.
RETURN
END
then the call statement is equivalent to
x = 1.
y = 3.
Note that the subroutine definition does not contain an assignment to the subprogram name, the way a function definition would have. For as far as the defining site is concerned, that is the only difference between a function and a subroutine.
Whereas a function was called by using its
name in the right-hand-side of an expression, a subroutine is invoked with
a CALL statement:
CALL subr(arg1,arg2,arg3)
When the call statement is encountered, control is transferred to the subroutine body. That is, the statements in the subroutine body are executed before the statement after the call is tackled.
A subroutine can have zero or more arguments. If the number of arguments is zero, no parentheses need be given both for the definition and the call (unlike with functions, where an empty pair is necessary both at the defining and using site).
Above we saw how subprograms are placed alongside, not inside, other subprograms and the main program. Among other things, this implies that Fortran subprograms are almost completely shielded from the main program. There is no immediate sharing of data.
Thus, any variables in the main program are (unless passed as argument
or placed in COMMON) invisible
to the subprograms. Conversely, any variables declared in a subprogram are
completely local to that subprogram. In particular, the same variable names
can be used, even with different defining types, in the main program and
subprograms. The same holds for labels.
A function for computing the factorial of an integer number would feature the following loop:
factor = 1
DO 10 i=1,n
factor = factor * i
10 CONTINUE
The variable n is the input, and factor is
the result to be returned, but i has no meaning outside the
computation of the factorial. Hence we make it a local variable:
FUNCTION factor(n)
INTEGER factor,n,i
factor = 1
DO 10 i=,n
factor = factor * i
10 CONTINUE
RETURN
END
Supplying a variable as argument to the invocation of a subprogram makes that variable known and accessible to the subprogram body. Example:
x = 1.5
CALL func(x)
...
SUBROUTINE func(arg)
...
arg = 2.3
...
END
When the execution of the statements in func starts, the
argument arg has value 1.5. When the subroutine's execution
has finished, the variable x in the main program has a value
of 2.3.
There are many points to be addressed in passing array arguments. This will be done in the section on arrays.
The parenthesised identifiers in the SUBROUTINE or FUNCTION
line are called the dummy arguments of the subprogram (this is actually
a somewhat silly terminology, only used in Fortran; most other languages
call these the formal arguments). These behave a bit like local
variables of the subprogram. You can for instance use them in any right-hand-side
expression. Whether you can use them on the left-hand-side depends on the
actual arguments.
Whatever is supplied in a CALL statement or in a function
call, corresponding to the dummy arguments are called the actual arguments.
As we saw in the examples above, actual arguments can be constants
or variables; other possibilities are expressions,
array elements, even subprogram names. In the case of variables (or array
elements, or in fact whole arrays),
the subprogram can have an assignment to the corresponding dummy variable,
though this is not recommended for functions;
in the case where the actual argument is a constant or expression, the language
standard prohibits assigning to the corresponding dummy argument.
Note that the compiler is unable to detect violations against this prohibition;
typically such programs as ftncheck
detect this kind of error. (This particular error could lead in certain
Fortran compilers to the value of constants being changed; certainly one
of the strangest bugs imaginable.)
The mechanism by which Fortran associates
actual arguments to dummy arguments is by reference; Fortran is
said to use `call by reference'. To understand this, consider that a variable
x is a reference to a value. Instead of talking about the value,
we only refer to it by a symbolic name. Supplying a variable as actual argument
to a subprogram makes the corresponding dummy argument another reference
to the same value.
Another error connected to passing arguments is `aliasing'. If a subroutine is called with identical variable actual arguments
CALL subr(x,x)
the corresponding dummy arguments are now aliases: in the subprogram
SUBROUTINE subr(a1,a2)
the dummy arguments a1 and a2 now refer to
the same value. This is no problem if they only appear in right-hand sides,
but in case of assignments
a1 = ....
a2 = ....
the result is most likely not what was intended.
It is possible for a subroutine or function to have no arguments. In that case the definitions look like
FUNCTION myfun()
...
SUBROUTINE mysub
...
and likewise, their applications look like
x = myfun()
...
call mysub
A function for computing a random number needs no input; it only returns a result:
FUNCTION random()
READ random
...
END
We compute the random number by remembering a seed value, and transforming this each time the function is called; the transformation needs two suitably chosen integers:
INTEGER seed,modulus,multiplier
SAVE seed
PARAMETER (modulus=27, multiplier=13)
seed = MOD(seed*multiplier,modulus)
The requested random number is then a scaling of the integer seed value;
we also use a DATA statement to initialise the seed:
DATA seed/4/
random = seed / REAL(modulus)
See below for a further elaboration of this example.
COMMONThe variables in Fortran subprograms or the main program are completely
invisible to other subprograms. We have already seen how variables can be
made accessible by passing it as arguments.
There is another way, namely by putting the data in a COMMON
block.
PROGRAM p
COMMON /global/x,y
...
END
SUBROUTINE s
COMMON /global/a,b
...
The common block is identified by its name; you are free to give the variables different names in different subprograms, but the number of variables should be the same every time.
In any common block, all variables should have the same type. You are allowed to mix scalar and array variables of the same type.
More than one common block can be placed on one line:
COMMON /rglobal/rmax,rmin,/iglobal/imax,imin
Data in common can be initialised with a DATA
statement, but this requires a special type of program unit: the BLOCK
DATA unit. This can only have non-executable
statements and it ends with END, without a RETURN.
Example:
BLOCK DATA initglobal
COMMON /rglobal/rmax,rmin,/iglobal/imax,imin
DATA imin,imax,rmin,rmax/1,-1,1.0,-1.0/
END
There can be more than one block data unit in a program.
All examples of COMMON blocks so far were of named
common; there is also blank common. Example:
COMMON x,y,z
While there can be arbitrarily many named common blocks, there can be
only one blank common block. Blank common can not be initialised in BLOCK DATA units.
In the example of a random number generator above, we initialised the
seed in a DATA statement. The problem with that is that the
sequence of random numbers will be the same each time the program is run.
To prevent this, we want a subroutine
CALL setrandomseed(5)
for setting the initial seed value. This subroutine communicates with
the random function by using a COMMON block:
SUBROURINTE setrandomseed(s)
INTEGER s,seed
COMMON /random/seed
seed = s
RETURN
END
and
FUNCTION random(n)
...
INTEGER seed
COMMON /random/seed
Note that the following is not allowed:
SUBROURINTE setrandomseed(seed)
INTEGER seed
COMMON /random/seed
When a subprogram's execution has finished, the values of all local variables become undefined, according to the language standard. In practice, many compilers will keep the values around, and the next time the subprogram is executed, the local variables will have this last value.
If you want to be sure variables retain their value from one incarnation
of the subprogram to the next, you have to declare them in a SAVE
statement. Example:
SUBROUTINE readbigger
SAVE biggest
READ *,rnew
IF (rnew.GT.biggest) THEN
biggest = rnew
PRINT *,'Biggest value so far: ',biggest
END IF
RETURN
END
In this and similar examples you do need to initialise the saved variable
in a DATA statement. For example, if all values to be read
are positive you could declare
SUBROUTINE readbigger
DATA /biggest/-1.0
SAVE biggest
...
Function and subroutine names can be passed as arguments to other subprograms.
For this, they first need to be declared at the calling site of the subprogram
they are being passed to, in an INTRINSIC
statement for library functions, and an EXTERNAL statement
for user-defined subprograms.
EXTERNAL myfun
...
res = integrate(myfun,0.0,1.0)
The EXTERNAL statement is a specification
statement.
You cannot declare statement functions as
EXTERNAL.
If the subroutine body contains a RETURN statement, this
causes control to be transferred back to the calling site; specifically,
the statement after the call will be executed. Example:
SUBROUTINE mysub
<statement block1>
IF (condition) RETURN
<statement block2>
RETURN
END
...
CALL mysub
<next statement>
The call statement causes the subroutine to be executed, specifically, the first block of statements is executed. If then the condition tested is true, execution of the subroutine body is terminated, and control is transferred back to the calling site. Specifically, the statement after the call is then executed.
There is also the
RETURN 3
syntax. This is not a recommended programming practice.
Some problems naturally call for a function referring to itself, eg n
factorial is n times n-1 factorial, with 1 factorial being 1. In Fortran77,
functions and subroutines are not allowed to call themselves, not even indirectly.
Usually the compiler will complain if you write a function that calls itself,
but if you have two subprograms, prog1 calling prog2,
and the other way around, and they are in separate files, the compiler cannot detect this `daisy-chain
recursion'.
Fortran90 does have recursion.