Fortran
Процедуры - Функции и подпрограммы
Поиск…
замечания
Функции и подпрограммы в сочетании с модулями являются инструментами для разбивки программы на единицы. Это делает программу более читаемой и управляемой. Каждый из этих блоков можно рассматривать как часть кода, который в идеале может быть скомпилирован и протестирован изолированно. Основная программа (ы) может вызывать (или вызывать) такие подпрограммы (функции или подпрограммы) для выполнения задачи.
Функции и подпрограммы различаются в следующем смысле:
- Функции возвращают один объект и, как правило, не изменяют значения его аргументов (т. Е. Действуют как математическая функция!);
- Подпрограммы обычно выполняют более сложную задачу, и они обычно изменяют свои аргументы (если они есть), а также другие переменные (например, те, которые указаны в модуле, который содержит подпрограмму).
Функции и подпрограммы коллективно идут под именем процедур . (В дальнейшем мы будем использовать глагол «вызов» как синоним «invoke», даже если технически процедуры, call
ed, являются subroutine
s, тогда как function
s отображается как правая часть задания или в выражениях.)
Синтаксис функции
Функции могут быть записаны с использованием нескольких типов синтаксиса
function name()
integer name
name = 42
end function
integer function name()
name = 42
end function
function name() result(res)
integer res
res = 42
end function
Функции возвращают значения через результат функции . Если оператор функции не имеет предложение result
функции имеет то же имя, что и функция. В result
функции получается из result
. В каждом из первых двух примеров выше результат функции дается по name
; в третьем - res
.
Результат функции должен быть определен во время выполнения функции.
Функции позволяют использовать некоторые специальные префиксы.
Чистая функция означает, что эта функция не имеет побочного эффекта:
pure real function square(x)
real, intent(in) :: x
square = x * x
end function
Элементная функция определяется как скалярный оператор, но она может быть вызвана с массивом как фактический аргумент, и в этом случае функция будет применяться по типу элемента. Если не указан impure
префикс (введенный в Fortran 2008), элементарная функция также является чистой функцией.
elemental real function square(x)
real, intent(in) :: x
square = x * x
end function
Оператор возврата
Оператор return
может использоваться для выхода из функции и подпрограммы. В отличие от многих других языков программирования он не используется для установки возвращаемого значения.
real function f(x)
real, intent(in) :: x
integer :: i
f = x
do i = 1, 10
f = sqrt(f) - 1.0
if (f < 0) then
f = -1000.
return
end if
end do
end function
Эта функция выполняет итеративное вычисление. Если значение f
становится отрицательным, функция возвращает значение -1000.
Рекурсивные процедуры
В функциях Fortran и подпрограммах должны быть явно объявлены как рекурсивные , если они должны называть себя снова, прямо или косвенно. Таким образом, рекурсивная реализация серии Фибоначчи может выглядеть так:
recursive function fibonacci(term) result(fibo)
integer, intent(in) :: term
integer :: fibo
if (term <= 1) then
fibo = 1
else
fibo = fibonacci(term-1) + fibonacci(term-2)
end if
end function fibonacci
Другим примером является возможность вычисления факториала:
recursive function factorial(n) result(f)
integer :: f
integer, intent(in) :: n
if(n == 0) then
f = 1
else
f = n * f(n-1)
end if
end function factorial
Для функции, непосредственно рекурсивно ссылающейся на себя, ее определение должно использовать суффикс result
. Функция не может быть как recursive
и elemental
.
Предположение о ложных аргументах
Атрибут intent
фиктивного аргумента в подпрограмме или функции объявляет о своем намеренном использовании. Синтаксис является либо одним из
intent(IN)
intent(OUT)
intent(INOUT)
Например, рассмотрим эту функцию:
real function f(x)
real, intent(IN) :: x
f = x*x
end function
intent(IN)
указывает, что фиктивный аргумент x
(не указатель) x
никогда не может быть определен или не определен во всей функции или ее инициализации. Если у фиктивного аргумента указателя есть intent(IN)
атрибута intent(IN)
, это относится к его ассоциации.
intent(OUT)
для аргумента фиктивного указателя без указателя означает, что фиктивный аргумент становится неопределенным при вызове подпрограммы (за исключением любых компонентов производного типа с инициализацией по умолчанию) и должен быть установлен во время выполнения. Фактический аргумент, переданный как фиктивный аргумент, должен быть определяемым: передача именованной или литеральной константы или выражения не допускается.
Аналогично предыдущему, если указатель фиктивный аргумент является intent(OUT)
статус ассоциации указателя становится неопределенным. Фактический аргумент здесь должен быть переменной указателя.
intent(INOUT)
указывает, что фактический аргумент является определяемым и подходит как для передачи, так и для возврата данных из процедуры.
Наконец, фиктивный аргумент может быть без атрибута intent
. Такой фиктивный аргумент используется ограниченным фактическим аргументом.
Например, рассмотрим
integer :: i = 0
call sub(i, .TRUE.)
call sub(1, .FALSE.)
end
subroutine sub(i, update)
integer i
logical, intent(in) :: update
if (update) i = i+1
end subroutine
Аргумент i
может иметь никакого атрибута intent
который разрешает оба вызова подпрограммы основной программы.
Ссылка на процедуру
Для полезности функции или подпрограммы она должна быть указана. Подпрограмма ссылается в заявлении о call
call sub(...)
и функцию внутри выражения. В отличие от многих других языков выражение не формирует полный оператор, поэтому ссылка на функцию часто встречается в инструкции присваивания или используется каким-то другим способом:
x = func(...)
y = 1 + 2*func(...)
Существует три способа указания ссылки на процедуру:
- как имя указателя процедуры или процедуры
- компонент процедуры объекта производного типа
- имя привязки процедуры привязки типа
Первое можно увидеть как
procedure(), pointer :: sub_ptr=>sub
call sub() ! With no argument list the parentheses are optional
call sub_ptr()
end
subroutine sub()
end subroutine
и последние два
module mod
type t
procedure(sub), pointer, nopass :: sub_ptr=>sub
contains
procedure, nopass :: sub
end type
contains
subroutine sub()
end subroutine
end module
use mod
type(t) x
call x%sub_ptr() ! Procedure component
call x%sub() ! Binding name
end
Для процедуры с фиктивными аргументами ссылка требует соответствующих фактических аргументов, хотя необязательные фиктивные аргументы могут не задаваться.
Рассмотрим подпрограмму
subroutine sub(a, b, c)
integer a, b
integer, optional :: c
end subroutine
На это могут ссылаться следующие два способа
call sub(1, 2, 3) ! Passing to the optional dummy c
call sub(1, 2) ! Not passing to the optional dummy c
Это так называемая позиционная ссылка: фактические аргументы связаны с позицией в списках аргументов. Здесь макет a
связан с 1
, b
с 2
и c
(если указано) с 3
.
Альтернативно, привязка ключевых слов может использоваться, когда процедура имеет явный интерфейс, доступный
call sub(a=1, b=2, c=3)
call sub(a=1, b=2)
который является тем же самым, что и выше.
Однако с ключевыми словами фактические аргументы могут предлагаться в любом порядке
call sub(b=2, c=3, a=1)
call sub(b=2, a=1)
Позиционные и ссылки на ключевые слова могут использоваться как
call sub(1, c=3, b=2)
пока задано ключевое слово для каждого аргумента, следующего за первым появлением ключевого слова
call sub(b=2, 1, 3) ! Not valid: all keywords must be specified
Значение ссылки на ключевые слова особенно заметно, когда имеется несколько необязательных аргументов фиктивного типа, как показано ниже, если в определении подпрограммы выше b
также были необязательными
call sub(1, c=3) ! Optional b is not passed
Списки аргументов для процедур с привязкой к типу или указатели процедур компонентов с переданным аргументом рассматриваются отдельно.