Here’s an example of the use of a function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ! $CLASSHG/codes/fortran/fcn1.f90
program fcn1
implicit none
real(kind=8) :: y,z
real(kind=8), external :: f
y = 2.
z = f(y)
print *, "z = ",z
end program fcn1
real(kind=8) function f(x)
implicit none
real(kind=8), intent(in) :: x
f = x**2
end function f
|
It prints out:
z = 4.00000000000000
Comments:
- A function returns a single value. Since this function is named f, the value of f must be set in the function somewhere. You cannot use f on the right hand side of any expression, e.g. you cannot set g = f in the function.
- f is declared external in the main program to let the compiler know it is a function defined elsewhere rather than a variable.
- The intent(in) statement tells the compiler that x is a variable passed into the function that will not be modified in the function.
- Here the program and function are in the same file. Later we will see how to break things up so each function or subroutine is in a separate file.
The input argument(s) to a function might also be modified, though this is not so common as using a subroutine as described below. But here’s an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | ! $CLASSHG/codes/fortran/fcn2.f90
program fcn2
implicit none
real(kind=8) :: y,z
real(kind=8), external :: f
y = 2.
print *, "Before calling f: y = ",y
z = f(y)
print *, "After calling f: y = ",y
print *, "z = ",z
end program fcn2
real(kind=8) function f(x)
implicit none
real(kind=8), intent(inout) :: x
f = x**2
x = 5.
end function f
|
This produces:
Before calling f: y = 2.00000000000000
After calling f: y = 5.00000000000000
z = 4.00000000000000
You are not required to specify the intent of each argument, but there are several good reasons for doing so:
- It helps catch bugs. If you specify intent(in) and then this variable is changed in the function or subroutine, the compiler will give an error.
- It can help the compiler produce machine code that runs faster. For example, if it is known to the compiler that some variables will never change during execution, this fact can be used.
In Fortran, subroutines are generally used much more frequently than functions. Functions are expected to produce a single output variable and examples like the one just given where an argument is modified are considered bad programming style.
Subroutines are more flexible since they can have any number of inputs and outputs. In particular they may have not output variable. For example a subroutine might take an array as an argument and print the array to some file on disk but not return a value to the calling program.
Here is a version of the same program as fcn1 above, that instead uses a subroutine:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ! $CLASSHG/codes/fortran/sub1.f90
program sub1
implicit none
real(kind=8) :: y,z
y = 2.
call fsub(y,z)
print *, "z = ",z
end program sub1
subroutine fsub(x,f)
implicit none
real(kind=8), intent(in) :: x
real(kind=8), intent(out) :: f
f = x**2
end subroutine fsub
|
Comments:
- Now we tell the compiler that x is an input variable and y is an output variable for the subroutine. The intent declarations are optional but sometimes help the compiler optimize code.
Here is a version that works on an array x instead of a single value:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ! $CLASSHG/codes/fortran/sub2.f90
program sub2
implicit none
real(kind=8), dimension(3) :: y,z
integer n
y = (/2., 3., 4./)
n = size(y)
call fsub(y,n,z)
print *, "z = ",z
end program sub2
subroutine fsub(x,n,f)
! compute f(x) = x**2 for all elements of the array x
! of length n.
implicit none
integer, intent(in) :: n
real(kind=8), dimension(n), intent(in) :: x
real(kind=8), dimension(n), intent(out) :: f
f = x**2
end subroutine fsub
|
This produces:
4.00000000000000 9.00000000000000 16.0000000000000
Comments:
- The length of the array is also passed into the subroutine. You can avoid this in Fortran 90 (see the next example below), but it was unavoidable in Fortran 77 and subroutines working on arrays in Fortran are often written so that the dimensions are passed in as arguments.
Here is a version that avoids passing the length of the array into the subroutine. In order for this to work some additional interface information must be specified. This is most easily done by including the subroutine in a module.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | ! $CLASSHG/codes/fortran/sub3.f90
module sub3module
contains
subroutine fsub(x,f)
! compute f(x) = x**2 for all elements of the array x.
implicit none
real(kind=8), dimension(:), intent(in) :: x
real(kind=8), dimension(size(x)), intent(out) :: f
f = x**2
end subroutine fsub
end module sub3module
!----------------------------------------------
program sub3
use sub3module
implicit none
real(kind=8), dimension(3) :: y,z
y = (/2., 3., 4./)
call fsub(y,z)
print *, "z = ",z
end program sub3
|
Comments:
See the section Fortran modules for more information about modules. Note in particular that the module must be defined first and then used in the program via the use statement.
The declaration of x in the subroutine uses dimension(:) to indicate that it is an array with a single index (having rank 1), without specifying how long the array is. If x was a rank 2 array indexed by x(i,j) then the dimension statement would be dimension(:,:).
The declaration of f in the subroutine uses dimension(size(x)) to indicate that it is an array with one index and the length of the array is the same as that of x. In fact it would be sufficient to tell the compiler that it is an array of rank 1:
real(kind=8), dimension(:), intent(out) :: fbut indicating that it has the same size ax x is useful for someone trying to understand the code.