Sometimes in a FORTRAN program we may have a section of code which we can make into a separate program known as a subprogram. Our main program can then make use of the subprogram, passing it pieces of information to use and getting back pieces of information.
There are several kinds of subprograms.
Subroutines
The syntax for a subroutine looks like this:
Subroutine Name(list of formal arguments) Implicit None ! declare each formal argument as a variable ! perhaps also declare some local variables ! executable code End Subroutine Name
When we make use of the subroutine, we use the instruction Call in the main program, like this:
Call Name(list of actual arguments)
There is some terminology here:
When the Call statement is executed, the list of actual arguments and the list of formal arguments are matched up, in order, left to right. The two lists should match in the number of arguments, their types, and the order in which they are listed.
Here is an example:
Subroutine Swap(A, B) Implicit None Integer :: A, B Integer :: Temp Temp = A A = B B = Temp End Subroutine Swap
This subroutine merely switches the values of two Integer arguments. We might need to do this repeatedly, so it is a good candidate for creating a separate subroutine.
Here A and B are the actual arguments and Temp is a local variable.
When we use the Swap subroutine, we might say:
Call Swap(X, Y)
For this to make sense, X and Y must be declared in the main program as Integer variables.
Some arguments may be used only as input and others only as output and others as both. If an argument is for output, we must make sure that the actual argument is an actual variable and not a constant or an expression.
Here is another (rather silly) example:
Subroutine FindSum(A, B, Sum) Implicit None Real :: A, B, Sum Sum = A + B End Subroutine FindSum
Here A and B are being used for input and Sum for output. We could call this subroutine several different ways, not all of which will work:
FindSum(23.5, 78.9, Total) FindSum(X, Y, Z+2.0)
Here the first call will succeed (we can change the value of Total) but the second call will fail (we cannot change the value of Z+2.0). Even worse, the second call will fail at run time; the compiler will not catch the problem.
There is a way around this: see the section below on the Intent clause.
Functions
A function is a kind of subprogram characterized by the fact that it computes a value for us. (It may do other things as well.) The syntax for it looks like this:
Type Function Name(list of formal arguments) Implicit None ! declare each formal argument as a variable ! perhaps also declare some local variables ! executable code including "Name = something" End Function Name
Here Name is used as an argument (for output only). It has a type, identified by Type on the first line as Real, Integer, etc.
The value computed by the function is provided by assigning a value to Name. We may have If statements and loops, but whatever happens, Name should be given a value.
Another way to write it is:
Function Name(list of formal arguments) Implicit None ! declare each formal argument and Name as a variable ! perhaps also declare some local variables ! executable code including "Name = something" End Function Name
Here is an example, similar to one of the implicit functions:
Real Function Abs(X) Real :: X If (X < 0.0) Then Abs = -1.0 * X Else Abs = X End If End Function Abs
We could use this in several ways:
Z = Abx(X + 3.9) Print *, Abs(C - D)
Statement functions
A statement function is a simple idea. It is basically just a formula to calculate, all in one statement. The syntax looks like this:
Name(list of formal arguments) = expression
Here the expression makes use of the formal arguments. Name and the formal arguments all need to be declared as usual. The expression can make use of other functions but cannot contain If statements or loops; it could be continued over several lines if necessary.
The definition of a statement function should be included in the top part of the program before any executable statement.
To use the statement function, we use it in the same fashion as an ordinary function.
Here is an example:
CylVol(R, H) = 3.1415926 * R**2 * H
We could use this in assignment statements, etc.
Statement functions are an old feature in FORTRAN and may possibly disappear in some future version of the language.
The Intent clause
As mentioned above, some formal arguments may be used in a subprogram only for input or only for output. We can indicate this to the compiler if we wish, and if we do, it will try to catch programming errors for us. (There is no guarantee involved.)
Here is an example:
Real Function BoxArea(H, W, L) Implicit None Real, Intent(In) :: H Real, Intent(In) :: W Real, Intent(In) :: L BoxArea = 2 * (H * W + W * L + L * H) End Function BoxArea
Here the Intent(In) clause tells the compiler that H, W and L are to be used for input only. If we stuck in a line somewhere such as
H = 2.5
the compiler would complain about it.
Likewise, we could have Intent(Out) for parameters that are to be used only for output, or we could habe Intent(InOut) to indicate a parameter is to be used both ways. In the Swap subroutine, A and B could be declared with Intent(InOut). In the FindSum example, Sum could be declared with Intent(Out).
The Intent clause makes sense only for formal arguments; it cannot be used for ordinary variables.
The Save statement
Under normal circumstances, local variables do not really exist other than when a subprogram is being executed. If I use a subprogram and local variable PDQ is initialized to the value 13 and is eventually given the value 42, the new value will not be remembered next time I use the subprogram and PDQ will again start out with the value 13.
This is sometimes not convenient. There are occasions when it would make sense to have a local variable and we want its value to be remembered from one time to the next.
The Save statement is what we need here.
Here is an example involving the printing of a page heading.
Subroutine PageHead(LineCount) Implicit None Integer :: LineCount Integer :: PageCount = 1 Save PageCount 100 Format(////, A, T62, A4, 1X, I2) Print 100, 'This is my page heading', 'Page, PageCount LineCount = 1 PageCount = PageCount + 1 End Subroutine PageHead
We might use this as follows:
If (LCount > 50) Call PageHead(LCount)
Here LCount is an Integer variable in the main program that counts lines per page. We want no more than 50 lines per page, so we call this subroutine as needed. (Here //// is used as a way to skip several lines to indicate a new page is wanted. In some FORTRANs, we could use a carriage control character '1' to do this.)
The local variable PageCount is used to put the page number in the heading. It is not needed anywhere else, so it makes sense as a local variable. PageCount is initialized to 1 and then it is incremented by 1 each time the subroutine is called.
The Save statement causes the value of PageCount to be saved between calls. If we did not have it in the subroutine, every page would be marked as page 1.
If we did not have the Save statement available, we could get along without it (in this case) by declaring PageCount as a variable in the main program and passing it as a second argument to PageHead.