Subprograms
A subprogram is a self-contained, but not standalone, program unit. It performs a specific task, usually by accepting parameters and returning a result to the unit that invokes (calls) it. Subprograms are essential to good coding practice. Among other benefits, they are
- Reusable. They can be called anywhere the task is to be performed.
- Easier to test and debug than a large, catch-all unit.
- Effective at reducing errors such as cut-and-paste mistakes.
Other general names for subprograms are routines, procedures, and methods. The word “method” is generally reserved for procedures defined within an object, but it is not conceptually different from any other subprogram.
Subprograms must be invoked or called in order for any of their code to be executed.
Functions and Subroutines
Unlike most languages, Fortran makes a distinction between functions and subroutines .
Functions take any number (up to compiler limits) of arguments and return one item. This item can be a compound type. Functions must be declared to a type like variables, or must be defined in an interface (prototype).
Subroutines take any number of arguments, up to the compiler limit, and return any number of arguments. All communication is through the argument list. If they are in the same file as the calling unit, subprograms follow the caller.
Variables in the argument list are often called dummy arguments since they stand for actual arguments that are defined in the calling unit. Like any variable, they must be declared explicitly. In Fortran this is done on separate lines, like for PROGRAM.
Subroutines
The subroutine unit begins with its name and parameter list
SUBROUTINE mysub(param1,param2,param3)
<type> :: param1, param1, param3
It must terminate with the END statement.
END [SUBROUTINE] [NAME]
The form END SUBROUTINE is highly recommended. This can be followed by the name of the subroutine
END SUBROUTINE mysub
but sometimes this leads to cut-and-paste errors. A RETURN statement is optional unless a premature return is desired. Invoking RETURN causes an immediate return of control to the caller. No other statements in the subprogram will be executed.
A subroutine is invoked through the CALL command
CALL mysub(var1, var2, var3)
Note that the names of the actual and dummy arguments need not match, but they must agree in number and type.
Functions
Functions are declared in a manner similar to subroutines, but unlike subroutines they have a type. The type of the function is the type of its return value.
<type> FUNCTION myfunc(param1,param2,param3)
<type> :: param1, param2, param3
A function receives its return value by assigning an expression to its name.
myfunc=param1*param2/param3
As for subroutines, a RETURN statement is optional except for returning control due to some conditional. They must also terminate with END
END [FUNCTION] [NAME]
Functions are invoked by name. They can enter into an expression anywhere on the right-hand side of the assignment operator (=).
z=4.*myfunc(var1,var2,var3)
As for subroutines, the names of the actual arguments need not be the same as those of the dummies, but the number and type must match.
Because functions have a type, they must be declared like a variable in any program unit that invokes them. Better yet, use an interface.
Subroutines have no return type and cannot be declared.
Example
PROGRAM myprog
REAL :: var1, var2, var3
REAL :: z
REAL :: myfunc
var1=11.; var2=12.; var3=13.
z=4.*myfunc(var1,var2,var3)
PRINT *, z
CALL mysub(var1,var2,var3)
PRINT *, var3
END PROGRAM
REAL FUNCTION myfunc(param1,param2,param3)
REAL, INTENT(IN) :: param1, param2, param3
if (param3 /= 0) then
myfunc=param1*param2/param3
else
myfunc=0.
endif
END FUNCTION
SUBROUTINE mysub(param1,param2,param3)
REAL, INTENT(IN) :: param1, param2
REAL, INTENT(OUT) :: param3
param3=param1-param2
END SUBROUTINE
Renaming Function Results
Normally the function value is returned by assigning a value to the name of the function. We can return it in a different variable with the RESULT clause.
FUNCTION summit(x,y) RESULT(s)
This is especially useful for recursive functions; it is required in this case until the F2008 standard, and not all compilers support F2008 in full yet. When using RESULT we declare the type of the name of the RESULT rather than the name of the function. The caller must still declare the function, however (or use an interface).
Example
FUNCTION myfunc(param1,param2) RESULT value
<type> :: value
<type>, INTENT(in) :: param1, param2
<type>, INTENT(in) :: param3, param4
statements
value=whatever
return !Optional unless premature
Exercise
Write a program that evaluates the function
$$f(x)=\frac{1}{\pi (1+x^2)}$$
for 401 values of x equally spaced between -4.0 and 4.0 inclusive.
Put the values into an array variable x
. Use variables for the starting and ending values of x and the number of values. Use an array operation to fill a variable y.
Write a function to evaluate f(x) for any given real (scalar) value of x and call it each time through your loop.
Print the values and the corresponding function evaluation to a comma-separated-values (CSV) file. Use software such as Excel, Python, Matlab, or anything else you know to plot the result.
Example Solution
PROGRAM myprog
INTEGER :: i,N
REAL :: x_start,x_end,incr,x,y
REAL :: func
x_start=-4.0
x_end = 4.0
N = 401
incr =(x_end-x_start)/(N+1)
open(10,file="function.csv")
x=x_start
do i=1,N
y=func(x)
write(10,'(f15.8,a,f15.8)') x,",",y
x=x+incr
enddo
END PROGRAM
REAL FUNCTION func(x)
REAL, INTENT(IN) :: x
REAL :: pi=4.0*atan(1.0)
func=1./(pi*(1.+x**2))
END FUNCTION