Fortran/Fortran procedures and functions
Part of the Fortran WikiBook
Functions and Subroutines
In most programs, a block of code is often re-used at several places. In order to minimize duplicating code and facilitate maintaining the code, such blocks of code should be placed within a function or subroutine. A Fortran
function is similar to a mathematical function, which takes one or many parameters as inputs and returns a single output value. A Fortran
subroutine is a block of code that performs some operation on the input variables, and as a result of calling the subroutine, the input variables are modified.
An expression containing a function call:
! func1 is a function defined elsewhere ! it takes an integer as an input and returns another integer as the output a = func1(b)
A call to a subroutine:
! sub1 is a subroutine defined elsewhere ! sub1 performs some operation on input variables e and f call sub1(e, f) ! now e or f, or both (or neither) may be modified
Many programming languages do not distinguish between functions and subroutines (e.g. C/C++, Python, Java). Pure functional programming languages (e.g. Haskell) only allow functions, because subroutines can, in some case, modify input variables as side-effects, which can complicate the code. In Fortran, functions and subroutines are different: the former returns a value while the latter does not.
Functions are simpler than subroutines. A function can only return one variable, and can be invoked from within expressions, like a
write statement, inside an if declaration
if (function) then, etc. A subroutine handles many variables and can only be used as a stand-alone command (using the keyword
In Fortran, one can use a
function to return a value or an array of values. The following program calls a function to compute the sum of the square and the cube of an integer.
function func(i) result(j) integer, intent(in) :: i ! input integer :: j ! output j = i**2 + i**3 end function func program xfunc implicit none integer :: i integer :: func i = 3 print*,"sum of the square and cube of",i," is",func(i) end program xfunc
intent(in) attribute of argument
i means that
i cannot be changed inside the function and in contrast, the return value
j has automatic
Note that the return type of
func needs to be declared.
If this is omitted, some compilers will not compile.
Open64 will compile the resulting code with warning, but the behaviour is ill-defined.
An alternative formulation (F77 compatible) is
FUNCTION func_name(a, b) INTEGER :: func_name INTEGER :: a REAL :: b func_name = (2*a)+b RETURN END FUNCTION PROGRAM cows IMPLICIT NONE INTEGER :: func_name PRINT *,func_name(2, 1.3) END PROGRAM
The return type of the
func_name still needs to be declared, as above. The only difference is how the return type of
func_name is referenced within
func_name . In this case, the return variable has the same name as the function itself.
Fortran requires you to declare recursive functions, such as a recursive factorial function, in order for the code to compile.
recursive function fact(i) result(j) integer, intent(in) :: i integer :: j if (i == 1) then j = 1 else j = i * fact(i - 1) end if end function fact
subroutine can be used to return several values through its arguments.
It is invoked with a
Here is an example.
subroutine square_cube(i,isquare,icube) integer, intent(in) :: i ! input integer, intent(out) :: isquare,icube ! output isquare = i**2 icube = i**3 end subroutine square_cube program xx implicit none integer :: i,isq,icub i = 4 call square_cube(i,isq,icub) print*,"i,i^2,i^3=",i,isq,icub end program xx
When declaring variables inside functions and subroutines that need to be passed in or out, intent may be added to the declaration.
intent(in) means that the variable value can enter, but not be changed
intent(out) means the variable is set inside the procedure and sent back to the main program with any initial values ignored.
intent(inout) means that the variable comes in with a value and leaves with a value (default).
More on Functions vs. Subroutines
Both functions and subroutines can modify their input variables. By necessity, subroutines modify input variables, since they do not return any output value. Functions do not have to, but are allowed, by default, to modify input variables. A function can be turned into a pure function, which does not have any side-effects through the use of the
intent attribute on all input variables, and further enforced through the keyword
pure keyword imposes additional restrictions, which essentially prevents the function from having any side-effects.)
An example of a
pure function square(x) real, intent(in) :: x real :: square square = x * x end function program main real :: a, b, square a = 2.0 b = square(a) ! After invoking the square(.) pure function, we can be sure that ! besides assigning the output value of square(a) to b, ! nothing else has been changed. end program main
One can use any order of the input arguments if one specifies them by their dummy name. That is possible as long as the calling procedure has an interface block of the intended procedure.
There is also a hybrid method where one specifies some parameters by position and the rest by their dummy name.
An example is given
function adder(a,b,c,d) real, intent(in) :: a,b,c,d real :: adder adder = a+b+c+d end function program main interface function adder(a,b,c,d) real, intent(in) :: a,b,c,d real :: adder end function end interface real :: my_sum my_sum = adder(d=1.0, b=2.0, c=1.0, a=1.0) ! specify each parameter by dummy name my_sum = adder(1.0, d=1.0, b=2.0, c=1.0) ! specify some parameters by dummy names, other by position end program main
Arguments can be set
The intrinsic function
present can be used to check if a specific parameter is set.
An example is given below.
function tester(a) real, intent(in), optional :: a real :: tester if ( present(a) ) then tester = a else tester = 0. end if end function tester program main interface function tester(a) real, intent(in), optional :: a real :: tester end function tester end interface print *, "[no args] tester():", tester() ! yields: 0.0 print *, "[args] tester(1.0):", tester(1.0) ! yields: 1.0 end program main
If a procedure has another procedure as dummy argument then one has to specify its type, just as the type of other parameters.
interface block is used for this case.
It consists of the procedure statement with the definitions of its arguments.
Note, that each interface block has its own scope.
Thus, if one needs to access outside values one needs to explicitly load them.
This can be achieved by the
import , or
An example is given below.
function tester(a) real, intent(in) :: a real :: tester tester = 2*a+3 end function tester program main interface function tester(a) real, intent(in) :: a real :: tester end function tester end interface print *, "tester(1.0):", tester(1.0) ! yields: 5.0 end program main
The value of a variable can be saved in-between procedure calls by explicitly giving the
An example is given below.
subroutine f() implicit none integer, save :: i = 0 i = i+1 print *,"value i:",i end program main implicit none interface subroutine f() integer, save :: i = 0 end end interface call f() ! yields: 1 call f() ! yields: 2 call f() ! yields: 3 end program main
It is possible to create generic functions with the same name for different input arguments, similar to the
abs function which works for integer, real, and complex data types.
The following example illustrates how to create a function
add which adds either two integers or character strings.
module add_mod implicit none private public :: add interface add procedure add_int, add_char end interface add contains pure function add_int( x, y ) integer, intent(in) :: x, y integer :: add_int add_int = x+y end function add_int pure function add_char( x, y ) character(len=*), intent(in) :: x, y character( len=len(x)+len(y) ), allocatable :: add_char add_char = x // y end function add_char end module add_mod program main use add_mod implicit none print *, "add ints: ", add( 1, 2 ) print *, "add chars: ", add( "abc", "def" ) end program main
One can set type-bound procedures of an abstract type as
deferred such that it needs to be reimplemented in derived types.
For more information see the section on abstract types.
One can create procedures that operate parameters of arbitrary dimension.
elemental is used where one defines the operation on a single object (e.g. integer) and the general case is automatically handled.
An example for the addition of arbitrary long integer dimension is given.
pure elemental function add_int( x, y ) integer, intent(in) :: x, y integer :: add_int add_int = x+y end function add_int program main implicit none interface pure elemental function add_int( x, y ) integer, intent(in) :: x, y integer :: add_int end function add_int end interface print *, "add ints:", add_int( 1, 2 ) ! yields: 3 print *, "add arrays:", add_int( [1, 2], [2, 3] ) ! yields: 3 5 end program main