Procedures
Classifications
A procedure in Fortran is either a subroutine or a function.
Subroutines
A subroutine named ab2bc_then_sumabc
is defined in the following program. INTENT(IN) attribute makes dummy arguments to be readable only. INTENT(OUT) attribute makes dummy arguments to be writable only. INTENT(INOUT) attribute makes dummy arguments to be readable and writable.
program main
use iso_fortran_env, only: dp => real64
implicit none
real(dp) :: a, b, c
a = 1.0_dp
b = 2.0_dp
c = 3.0_dp
call ab2bc_then_sumabc(a, b, c)
print *, a, b, c
end program main
subroutine ab2bc_then_sumabc(a, b, c)
use iso_fortran_env, only: dp => real64
implicit none
real(dp), intent(in) :: a
real(dp), intent(inout) :: b
real(dp), intent(out) :: c
real(dp) :: s
c = b
b = a
s = a + b + c
print *, s
end subroutine ab2bc_then_sumabc
Functions
Two functions named combinatorial
and factorial
respectively are defined in the following program. The variables in result()
are the results of functions. You should make all dummy arguments (The result is not defined as a dummy argument) to be readable only, or you are likely to get into trouble.
program main
implicit none
integer :: combinatorial
print *, combinatorial(7, 3)
end program main
function combinatorial(n, m) result(comb)
implicit none
integer, intent(in) :: n
integer, intent(in) :: m
integer :: comb
integer :: factorial
comb = factorial(n) &
/ (factorial(m)*factorial(n-m))
end function combinatorial
function factorial(n) result(fact)
implicit none
integer, intent(in) :: n
integer :: fact
integer :: i
fact = 1
do i = 1, n
fact = fact * i
end do
end function factorial
Procedure interface
The interface of a procedure is either explicit or implicit. The procedure defined by a separate subprogram has an implicit interface in default, while The procedure defined in a module or submodule has an explicit interface.
Explicit interface
If you use a procedure which is “complicated”, or use a procedure in a “complicated” way, you need to use an interface block to make the interface of the procedure explicit. An interface block is similar to an interface declaration in a C header file. All situations in which you need an interface block are listed in paragraph 1 of subsubsection 15.4.2.2 of Fortran 2023 interpretation document.
In the following program, we used a keyword argument, and therefore we have to add an interface block. The interface block must be added in the specification part, and USE statements and implicit none
must be added in the interface block for necessity.
program main
use iso_fortran_env, only: dp => real64
implicit none
real(dp) :: x, y, z
interface
subroutine ab2bc_then_sumabc(a, b, c)
use iso_fortran_env, only: dp => real64
implicit none
real(dp), intent(in) :: a
real(dp), intent(inout) :: b
real(dp), intent(out) :: c
end subroutine ab2bc_then_sumabc
end interface
x = 1.0_dp
y = 2.0_dp
z = 3.0_dp
call ab2bc_then_sumabc(x, y, c=z)
print *, x, y, z
end program main
subroutine ab2bc_then_sumabc(a, b, c)
use iso_fortran_env, only: dp => real64
implicit none
real(dp), intent(in) :: a
real(dp), intent(inout) :: b
real(dp), intent(out) :: c
real(dp) :: s
c = b
b = a
s = a + b + c
print *, s
end subroutine ab2bc_then_sumabc
Abstract interface
An abstract interface is a set of procedure characteristics with the dummy argument names. In the following program, we first defined an abstract interface dim2mat
, and then used procedure(dim2mat)
to declare explicit interfaces of function eye
and minkowski
, which have the same procedure characteristics with abstract interface dim2mat
.
program main
use iso_fortran_env, only: dp => real64
implicit none
abstract interface
function dim2mat(dim) result(mat)
use iso_fortran_env, only: dp => real64
implicit none
integer, intent(in) :: dim
real(dp) :: mat(0:dim-1, 0:dim-1)
end function dim2mat
end interface
procedure(dim2mat) :: eye, minkowski
real(dp), dimension(0:3, 0:3) :: mat_i, mat_m
mat_i = eye(4)
mat_m = minkowski(4)
end program main
function eye(n) result(mat)
use iso_fortran_env, only: dp => real64
implicit none
integer, intent(in) :: n
real(dp) :: mat(0:n-1, 0:n-1)
integer :: i
mat = 0.0_dp
do i = 0, n-1
mat(i, i) = 1.0_dp
end do
end function eye
function minkowski(n) result(eta)
use iso_fortran_env, only: dp => real64
implicit none
integer, intent(in) :: n
real(dp) :: eta(0:n-1, 0:n-1)
integer :: i
eta = 0.0_dp
eta(0, 0) = -1.0_dp
do i = 1, n-1
eta(i, i) = 1.0_dp
end do
end function minkowski
Generic interface
We can use generic interface to “produce a generic procedure”. In the following program, we defined a generic interface sgn
, and if we use “the generic procedure sgn
”, the arguments will be automatically associated to argument in sgn_real64
or sgn_real128
.
program main
use iso_fortran_env, only: dp => real64, &
qp => real128
implicit none
interface sgn
function sgn_real64(x) result(y)
use iso_fortran_env, only: dp => real64
implicit none
real(dp), intent(in) :: x
real(dp) :: y
end function sgn_real64
function sgn_real128(x) result(y)
use iso_fortran_env, only: qp => real128
implicit none
real(qp), intent(in) :: x
real(qp) :: y
end function sgn_real128
end interface
print *, sgn(10.0_dp)
print *, sgn(10.0_qp)
end program main
function sgn_real64(x) result(y)
use iso_fortran_env, only: dp => real64
implicit none
real(dp), intent(in) :: x
real(dp) :: y
if (x > 0.0_dp) then
y = 1.0_dp
else if (x < 0.0_dp) then
y = -1.0_dp
else
y = 0.0_dp
end if
end function sgn_real64
function sgn_real128(x) result(y)
use iso_fortran_env, only: qp => real128
implicit none
real(qp), intent(in) :: x
real(qp) :: y
if (x > 0.0_qp) then
y = 1.0_qp
else if (x < 0.0_qp) then
y = -1.0_qp
else
y = 0.0_qp
end if
end function sgn_real128
Defined assignment, operations, and I/O
See subsubsubsection 15.4.3.4.2–15.4.3.4.4 of Fortran 2023 interpretation document for details.
Attributes of dummy arguments
OPTIONAL attribute
A dummy argument with OPTIONAL attribute is an optional dummy argument. Intrinsic function present
can be used to check whether an optional dummy argument is present.
program main
implicit none
interface
subroutine example(arg1, arg2, arg3, arg4, arg5)
integer, intent(in) :: arg1
integer, intent(in), optional :: arg2
integer, intent(in) :: arg3
integer, intent(in), optional :: arg4
integer, intent(in) :: arg5
end subroutine example
end interface
call example(1, 2, 3, 4, 5)
call example(1, 2, arg5=5, arg3=3)
call example(arg5=5, arg3=3, arg4=4, arg1=1)
end program main
subroutine example(arg1, arg2, arg3, arg4, arg5)
integer, intent(in) :: arg1
integer, intent(in), optional :: arg2
integer, intent(in) :: arg3
integer, intent(in), optional :: arg4
integer, intent(in) :: arg5
print *, 'arg1 =', arg1
if (present(arg2)) then
print *, 'arg2 =', arg2
end if
print *, 'arg3 =', arg3
if (present(arg4)) then
print *, 'arg4 =', arg4
end if
print *, 'arg5 =', arg5
end subroutine example
SAVE attribute
A dummy argument with SAVE attribute is a saved dummy argument. The value of a saved dummy argument will be kept even if the procedure have returned. A saved dummy argument in Fortran is equivalent to a static local dummy argument in C.
program main
use iso_fortran_env, only: dp => real64
implicit none
call count() ! 0 -> 1
call count() ! 1 -> 2
call count() ! 2 -> 3
end program main
subroutine count()
implicit none
integer, save :: n = 0
n = n + 1
print *, n
end subroutine count
Character dummy argument
Use *
or len=*
to make a character dummy argument to be an assumed-length character, and its length can be automatically assumed from that of the effective actual argument.
program main
implicit none
character(3), parameter :: char_in = 'Hi!'
character(1) :: char_out
print *, char_in
call to_screen(char_in, char_out)
print *, char_out
end program main
subroutine to_screen(char_in, char_out)
implicit none
character(*), intent(in) :: char_in
character(*), intent(out) :: char_out
print *, char_in, len(char_in)
char_out = char_in
print *, char_out, len(char_out)
end subroutine to_screen
Array dummy argument
Explicit-shape array
In the following program, r
is a normal explicit-shape array.
program main
use iso_fortran_env, only: dp => real64
implicit none
real(dp) :: r(2)
real(dp) :: norm_1
r = [-1.0_dp, 2.0_dp]
print *, norm_1(r)
end program main
function norm_1(r) result(norm)
use iso_fortran_env, only: dp => real64
implicit none
real(dp), intent(in) :: r(2)
real(dp) :: norm
norm = sum(abs(r))
end function norm_1
In the following program, r
is an adjustable array, which is defined as a kind of explicit-shape array.
program main
use iso_fortran_env, only: dp => real64
implicit none
real(dp) :: r_2(2), r_3(3)
real(dp) :: norm_1
r_2 = [-1.0_dp, 2.0_dp]
r_3 = [-1.0_dp, 2.0_dp, -3.0_dp]
print *, norm_1(r_2, size(r_2))
print *, norm_1(r_3, size(r_3))
end program main
function norm_1(r, size_r) result(norm)
use iso_fortran_env, only: dp => real64
implicit none
real(dp), intent(in) :: r(size_r)
integer, intent(in) :: size_r
real(dp) :: norm
norm = sum(abs(r))
end function norm_1
In the following program, mat
is an automatic array, which is defined as a kind of explicit-shape array. The difference between adjustable array and automatic array is that adjustable array is while automatic array is not dummy argument.
program main
use iso_fortran_env, only: dp => real64
implicit none
real(dp) :: strange_mat_element_sum
print *, strange_mat_element_sum(50)
end program main
function strange_mat_element_sum(n) result(s)
use iso_fortran_env, only: dp => real64
implicit none
integer, intent(in) :: n
real(dp) :: s
real(dp) :: mat(n, n)
integer :: i, j
do i = 1, n
do j = 1, n
mat(i, j) = sqrt(real(i+j-1))
end do
end do
s = sum(mat)
end function strange_mat_element_sum
Assumed-shape array
In the following program, r
is an assumed-shape array.
program main
use iso_fortran_env, only: dp => real64
implicit none
real(dp) :: r_2(2), r_3(3)
real(dp) :: norm_1
r_2 = [-1.0_dp, 2.0_dp]
r_3 = [-1.0_dp, 2.0_dp, -3.0_dp]
print *, norm_1(r_2)
print *, norm_1(r_3)
end program main
function norm_1(r) result(norm)
use iso_fortran_env, only: dp => real64
implicit none
real(dp), intent(in) :: r(:)
real(dp) :: norm
norm = sum(abs(r))
end function
The lower bounds of indexes of assumed-shape array can be determined as in the following program.
program main
use iso_fortran_env, only: dp => real64
implicit none
real(dp) :: r_2(2, 1), r_3(3, 1)
interface
function norm_1(r) result(norm)
use iso_fortran_env, only: dp => real64
implicit none
real(dp), intent(in) :: r(0:,:)
real(dp) :: norm
end function
end interface
r_2 = reshape([0.0_dp, -1.0_dp], [2, 1])
r_3 = reshape([0.0_dp, -1.0_dp, 2.0_dp], [3, 1])
print *, norm_1(r_2)
print *, norm_1(r_3)
end program main
function norm_1(r) result(norm)
use iso_fortran_env, only: dp => real64
implicit none
real(dp), intent(in) :: r(0:,:)
real(dp) :: norm
norm = sum(abs(r))
end function norm_1
Dummy procedure
In the following program, f
is a dummy procedure.
program main
use iso_fortran_env, only: dp => real64
implicit none
interface
function identity(x) result(y)
use iso_fortran_env, only: dp => real64
implicit none
real(dp), intent(in) :: x
real(dp) :: y
end function
end interface
real(dp) :: integrate
print *, integrate(identity, 0.0_dp, 1.0_dp)
end program main
function integrate(f, a, b) result(s)
! Use trapezoidal rule.
use iso_fortran_env, only: dp => real64
implicit none
interface
function f(x) result(y)
use iso_fortran_env, only: dp => real64
implicit none
real(dp), intent(in) :: x
real(dp) :: y
end function
end interface
real(dp), intent(in) :: a
real(dp), intent(in) :: b
real(dp) :: s
real(dp) :: h
integer :: i
h = (b-a) / 10000
s = (f(a)+f(b)) / 2
do i = 1, 9999
s = s + f(a+i*h)
end do
s = s * h
end function integrate
function identity(x) result(y)
use iso_fortran_env, only: dp => real64
implicit none
real(dp), intent(in) :: x
real(dp) :: y
y = x
end function identity
Special procedures
Pure procedures
Characteristics of pure procedures are listed in section 15.7 of Fortran 2023 interpretation document. A representative pure procedure is a procedure with only INTENT(IN) attribute, and I/O are not possible to happen during the execution of the procedure. If you understand functional programming, you will easily understand pure procedures. Add pure
in front of subroutine
or function
in the start statement to make procedure pure as in following program.
program main
implicit none
integer :: factorial
print *, factorial(3)
end program main
pure function factorial(n) result(p)
integer, intent(in) :: n
integer :: p
integer :: i
p = 1
do i = 1, n
p = p * i
end do
end function factorial
Simple procedures
Characteristics of simple procedures are listed in section 15.8 of Fortran 2023 interpretation document. A representative simple procedure is a pure procedure which don’t reference any procedure which is not simple procedure. Add simple
, which implies pure
, in front of subroutine
or function
in the start statement to make procedure simple as in following program. Simple procedures are too new, and your compiler may not allow definitions of them.
program main
implicit none
integer :: factorial
print *, factorial(3)
end program main
simple function factorial(n) result(p)
integer, intent(in) :: n
integer :: p
integer :: i
p = 1
do i = 1, n
p = p * i
end do
end function factorial
Elemental procedures
Characteristics of elemental procedures are listed in section 15.9 of Fortran 2023 interpretation document. A representative elemental procedure is a procedure which is similar to a “ufunc” in Numpy. Add elemental
, which implies pure
, in front of subroutine
or function
in the start statement to make procedure elemental as in following program.
program main
implicit none
integer :: i
interface
elemental function factorial(n) result(p)
integer, intent(in) :: n
integer :: p
end function factorial
end interface
print *, factorial(3)
print *, factorial([(i, i = 1, 9)])
end program main
elemental function factorial(n) result(p)
integer, intent(in) :: n
integer :: p
integer :: i
p = 1
do i = 1, n
p = p * i
end do
end function factorial
If you want to specify that an elemental procedure is impure, additionally add impure
in front of subroutine
or function
in the start statement.
program main
implicit none
integer :: i
interface
impure elemental function factorial(n) result(p)
integer, intent(in) :: n
integer :: p
end function factorial
end interface
print *, factorial(3)
print *, factorial([(i, i = 1, 9)])
end program main
impure elemental function factorial(n) result(p)
integer, intent(in) :: n
integer :: p
integer :: i
p = 1
do i = 1, n
p = p * i
end do
print *, p
end function factorial
Recursive procedures
A recursive procedure is a procedure which can reference itself. Add recursive
in front of subroutine
or function
in the start statement to make procedure recursive as in following program.
program main
implicit none
integer :: factorial
print *, factorial(3)
end program main
recursive function factorial(n) result(p)
integer, intent(in) :: n
integer :: p
if (n == 1) then
p = 1
else
! factorial(n) == n * factorial(n-1)
p = n * factorial(n-1)
end if
end function factorial
If you want to specify that an elemental procedure is pure, simple, or elemental, additionally add pure
, simple
, or elemental
in front of subroutine
or function
in the start statement.
program main
implicit none
integer :: i
interface
elemental recursive function factorial(n) result(p)
integer, intent(in) :: n
integer :: p
end function factorial
end interface
print *, factorial(3)
print *, factorial([(i, i = 1, 9)])
end program main
elemental recursive function factorial(n) result(p)
integer, intent(in) :: n
integer :: p
if (n == 1) then
p = 1
else
! factorial(n) == n * factorial(n-1)
p = n * factorial(n-1)
end if
end function factorial