Fortran allows one procedure to be used as the actual argument of another procedure. This provides a powerful facility, though one that most programmers use only rarely. Procedures are normally used to carry out a given set of operations on different sets of data; but sometimes you want to carry out the same set of operations on different functional forms. Examples include: finding the gradient of a function, integrating the area under a curve, or simply plotting a graph. If the curve is specified as a set of data points then you can simply pass over an array, but if it is specified by means of some algorithm then the procedure which evaluates it can itself be an actual argument.
In the next example, the subroutine GRAPH plots a graph of a function MYFUNC between specified limits, with its argument range divided somewhat arbitrarily into 101 points. For simplicity it assumes the existence of a subroutine PLOT which moves the pen to position (X,Y). Some other subroutines would, in practice, almost certainly be required.
SUBROUTINE GRAPH(MYFUNC, XMIN, XMAX)
*Plots functional form of MYFUNC(X) with X in range XMIN:XMAX.
REAL MYFUNC, XMIN, XMAX
XDELTA = (XMAX - XMIN) / 100.0
DO 25, I = 0,100
X = XMIN + I * XDELTA
Y = MYFUNC(X)
CALL PLOT(X, Y)
25 CONTINUE
END
The procedure GRAPH can then be used to plot a function simply
by providing its name them as the first argument of the call. The
only other requirement is that the name of each function used as an
actual argument in this way must be specified in an INTRINSIC or
EXTERNAL statement, as appropriate. Thus:
PROGRAM CURVES
INTRINSIC SIN, TAN
EXTERNAL MESSY
CALL GRAPH(SIN, 0.0, 3.14159)
CALL GRAPH(TAN, 0.0, 0.5)
CALL GRAPH(MESSY, 0.1, 0.9)
END
REAL FUNCTION MESSY(X)
MESSY = COS(0.1*X) + 0.02 * SIN(SQRT(X))
END
This will first plot a graph of the sine function, then of the tangent
function with a different range, and finally produce another plot of
the external function called MESSY. These functions must, of
course, have the same procedure interface themselves and must be
called correctly in the GRAPH procedure.
It is possible to pass either a function or a subroutine as an actual argument in this way: the only difference is that a CALL statement is used instead of a function reference to execute the dummy procedure. It is possible to pass a procedure through more than one level of procedure call in the same way. Continuing the last example, another level could be introduced like this:
PROGRAM CURVE2
EXTERNAL MESSY
INTRINSIC SIN, TAN
CALL GRAPH2(PRETTY)
CALL GRAPH2(TAN)
END
SUBROUTINE GRAPH2(PROC)
EXTERNAL PROC
CALL GRAPH(PROC, 0.1, 0.7)
END
Thus the procedure GRAPH2 sets limits to each plot and passes
the procedure name on to GRAPH. The symbolic name PROC
must be declared in an EXTERNAL statement as it is a dummy
procedure: an EXTERNAL statement is required whether the
actual procedure at the top level is intrinsic or external. The syntax
of the INTRINSIC and EXTERNAL statements is given in section
9.12 below.
The name of an intrinsic function used as an actual argument must be a specific name and not a generic one. This is the only circumstance in which you still have to use specific names for intrinsic functions. A full list of specific names is given in the appendix. A few of the most basic intrinsic functions which are often expanded to in-line code (those for type conversion, lexical comparison, as well as MIN and MAX) cannot be passed as actual arguments.