Fortran/Fortran procedures and functions
Functions and Subroutines
[edit | edit source]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.
Functions are simpler than subroutines. A function must return a single value, and can be invoked from within expressions, like a write
statement, inside an if declaration if (function) then
, etc. A subroutine does not return a value, but can return many values via its arguments and can only be used as a stand-alone command (using the keyword call
).
Function
[edit | edit source]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
program main
implicit none
integer :: i
integer :: func
i = 3
print *, "sum of the square and cube of", i, "is", func(i)
end program
The intent (in)
attribute of argument i
means that i
cannot be changed inside the function and in contrast, the return value j
has automatic intent (out)
. 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 behavior 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.
Recursion
[edit | edit source]Recursive functions can be declared , in a way such as the one shown below, 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
[edit | edit source]A subroutine
can be used to return several values through its arguments. It is invoked with a call
statement. 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
program main
implicit none
external square_cube ! external subroutine
integer :: isq, icub
call square_cube(4, isq, icub)
print *, "i,i^2,i^3=", 4, isq, icub
end program
Intent
[edit | edit source]When declaring variables inside functions and subroutines that need to be passed in or out, intent may be added to the declaration. The default is no intent checking - which can allow erroneous coding to be undetected by the compiler.
intent (in)
- the value of the dummy argument may be used, but not modified, within the procedure.
intent (out)
- the dummy argument may be set and then modified within the procedure, and the values returned to the caller.
intent (inout)
- initial values of the dummy argument may be both used and modified within the procedure, and then returned to the caller.
More on Functions vs. Subroutines
[edit | edit source]Different function result definitions
[edit | edit source]Functions can define the data type of their result in different forms: either as a separate variable or by the function name.
See the examples below
function f1(i) result (j)
!! result's variable: separately specified
!! result's data type: separately specified
integer, intent (in) :: i
integer :: j
j = i + 1
end function
integer function f2(i) result (j)
!! result's variable: separately specified
!! result's data type: by prefix
integer, intent (in) :: i
j = i + 2
end function
integer function f3(i)
!! result's variable: by function name
!! result's data type: by prefix
integer, intent(in) :: i
f3 = i + 3
end function
function f4(i)
!! result's variable: by function name
!! result's data type: separately specified
integer, intent (in) :: i
integer :: f4
f4 = i + 4
end function
program main
implicit none
integer :: f1, f2, f3, f4
print *, 'f1(0)', f1(0) ! output: 1
print *, 'f2(0)', f2(0) ! output: 2
print *, 'f3(0)', f3(0) ! output: 3
print *, 'f4(0)', f4(0) ! output: 4
end program
External
[edit | edit source]Procedures must be included by module use
or by specifying them as external
procedures. external
supplies only an implicit interface which is inferior as the compiler doesn't know the number of arguments and neither their data types. Thus, it cannot yield warnings at compile time (in contrast to an explicit interface given from a module use
, c.f. Fortran/OOP in Fortran).
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
integer function pow4(i)
integer, intent (in) :: i
pow4 = i**4
end function
program main
implicit none
external square_cube ! external subroutine (only implicit interface)
integer :: pow4 ! external function (only implicit interface)
integer :: i, isq, icub
i = 5
call square_cube(i, isq, icub)
print '(A,4I5)', "i,i^2,i^3,i^4=", i, isq, icub, pow4(i)
end program
Pure procedures
[edit | edit source]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
. The pure
keyword imposes additional restrictions, which essentially prevents the function from having any side-effects.
An example of a pure
function.
pure real function square(x)
real, intent (in) :: x
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
Keyword arguments
[edit | edit source]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 (which is automatically created if one includes the function by module
usage uses modules).
There is also a hybrid method where one specifies some parameters by position and the rest by their dummy name.
An example is given
real function adder(a,b,c,d)
real, intent (in) :: a, b, c, d
adder = a+b+c+d
end function
program main
interface
real function adder(a,b,c,d)
real, intent (in) :: a, b, c, d
end function
end interface
print *, adder(d=1.0, b=2.0, c=1.0, a=1.0) ! specify each parameter by dummy name
print *, adder(1.0, d=1.0, b=2.0, c=1.0) ! specify some parameters by dummy names, other by position
end program
Optional arguments
[edit | edit source]Arguments can be set optional
. The intrinsic function present
can be used to check if a specific parameter is set.
An example is given below.
real function tester(a)
real, intent (in), optional :: a
if (present(a)) then
tester = a
else
tester = 0.0
end if
end function
program main
interface
real function tester(a)
real, intent (in), optional :: a
end function
end interface
print *, "[no args] tester() :", tester() ! yields: 0.0
print *, "[ args] tester(1.0):", tester(1.0) ! yields: 1.0
end program
Interface block
[edit | edit source]If a procedure has another procedure as dummy argument then one has to specify its type, just as the type of other parameters. An 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 use
statements.
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
Save attribute
[edit | edit source]The value of a variable can be saved in-between procedure calls by explicitly giving the save
attribute.
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
Generic
[edit | edit source]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
Deferred
[edit | edit source]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.
Elemental
[edit | edit source]One can create procedures that operate parameters of arbitrary dimension. The keyword 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