Организация структуры кода#
Большинство языков программирования позволяют объединять часто используемый код в подпрограммы, которые можно повторно использовать, вызывая их из других частей кода.
В языке Fortran доступно два вида подпрограмм:
Процедура: вызывается оператором
call
Функция: вызывается внутри выражения или присваивания, которому она возвращает значение
И процедуры, и функции имеют доступ к переменным в родительской области видимости по ассоциации имён аргументов; если не указан атрибут value
, то это похоже на вызов по ссылке.
Процедуры#
Входные аргументы процедур, известные как фиктивные (пустые) аргументы, указываются внутри круглых скобок после имени процедуры; типы и атрибуты фиктивных аргументов объявляются в теле процедуры так же, как и её локальные переменные.
Пример:
! Print matrix A to screen
subroutine print_matrix(n,m,A)
implicit none
integer, intent(in) :: n
integer, intent(in) :: m
real, intent(in) :: A(n, m)
integer :: i
do i = 1, n
print *, A(i, 1:m)
end do
end subroutine print_matrix
Обратите внимание на дополнительный атрибут intent
при объявлении фиктивных аргументов; этот необязательный атрибут указывает компилятору, доступен ли аргумент „только для чтения“ (intent(in)
), „только для записи“ (intent(out)
) или „для чтения и записи“ (intent(inout)
) внутри процедуры. В этом примере процедура не изменяет свои аргументы, поэтому все аргументы имеют атрибут intent(in)
.
Хорошей практикой является всегда указывать атрибут
intent
для фиктивных аргументов; это позволяет компилятору найти непреднамеренные ошибки и обеспечивает самодокументирование кода.
Мы можем вызвать процедуру из программы с помощью оператора call
:
program call_sub
implicit none
real :: mat(10, 20)
mat(:,:) = 0.0
call print_matrix(10, 20, mat)
end program call_sub
Этот пример использует так называемый аргумент в виде explicit-shape массива (явной формы), так как мы передали дополнительные переменные для описания размеров массива
A
; это не потребуется, если мы поместим нашу процедуру в модуль, как описано ниже.
Функции#
! L2 Norm of a vector
function vector_norm(n,vec) result(norm)
implicit none
integer, intent(in) :: n
real, intent(in) :: vec(n)
real :: norm
norm = sqrt(sum(vec**2))
end function vector_norm
В коде программного продукта следует, вместо приведённой в примере, использовать встроенную функцию
norm2
.
Чтобы вызвать эту функцию:
program run_fcn
implicit none
real :: v(9)
real :: vector_norm
v(:) = 9
print *, 'Vector norm = ', vector_norm(9,v)
end program run_fcn
Хорошей практикой программирования является когда функция не изменяет свои аргументы — то есть, все аргументы функции должны иметь атрибут
intent(in)
. Такие функции называютpure
(чистыми) функциями. Используйте процедуры, если ваша подпрограмма должна изменять свои аргументы.
Модули#
Модули языка Fortran содержат определения, которые становятся доступными для программ, подпрограмм и других модулей с помощью оператора use
. Они могут содержать объекты данных, определения типов, подпрограммы и интерфейсы.
Модули позволяют управлять областью видимости, когда доступ к объектам организуют явно
Модули автоматически генерируют явные интерфейсы, необходимые для современных подпрограмм
Рекомендуется всегда размещать функции и процедуры внутри модулей.
Пример:
module my_mod
implicit none
private ! All entities are now module-private by default
public public_var, print_matrix ! Explicitly export public entities
real, parameter :: public_var = 2
integer :: private_var
contains
! Print matrix A to screen
subroutine print_matrix(A)
real, intent(in) :: A(:,:) ! An assumed-shape dummy argument
integer :: i
do i = 1, size(A,1)
print *, A(i,:)
end do
end subroutine print_matrix
end module my_mod
Compare this
print_matrix
subroutine with that written outside of a module. We no longer have to explicitly pass the matrix dimensions and can instead take advantage of assumed-shape arguments since the module will generate the required explicit interface for us. This results in a much simpler subroutine interface.
Чтобы подключить модуль внутри программы, используется оператор use
:
program use_mod
use my_mod
implicit none
real :: mat(10, 10)
mat(:,:) = public_var
call print_matrix(mat)
end program use_mod
Пример: явный список импорта
use my_mod, only: public_var
Пример: импорт по псевдониму
use my_mod, only: printMat=>print_matrix
Каждый модуль должен быть написан в отдельном файле исходного кода
.f90
. Модули должны быть скомпилированы до компиляции любых программных блоков, которые их подключают операторомuse
.
Optional arguments#
An advantage of placing subroutines and functions in modules is that they can have optional
arguments. In a procedure with an argument declared optional, the present
function is used to test if the argument was set in the caller. Optional arguments that are not present may not be accessed within the procedure. Here is a generalization of the vector_norm
function that can use powers other than 2 to compute the Lp norm.
module norm_mod
implicit none
contains
function vector_norm(vec,p) result(norm)
real, intent(in) :: vec(:)
integer, intent(in), optional :: p ! power
real :: norm
if (present(p)) then ! compute Lp norm
norm = sum(abs(vec)**p) ** (1.0/p)
else ! compute L2 norm
norm = sqrt(sum(vec**2))
end if
end function vector_norm
end module norm_mod
program run_fcn
use norm_mod
implicit none
real :: v(9)
v(:) = 9
print *, 'Vector norm = ', vector_norm(v), vector_norm(v,2)
print *, 'L1 norm = ', vector_norm(v,1)
end program run_fcn