Правильное использование модулей, подпрограмм и функций в Fortran
Недавно я узнал о блоках tinterface при добавлении функции в свою программу Fortran. Все работает красиво и аккуратно, но теперь я хочу добавить вторую функциональность в блок интерфейса.
Вот мой блок интерфейса:
interface
function correctNeighLabel (A,i,j,k)
integer :: correctNeighLabel
integer, intent(in) :: i,j,k
integer,dimension(:,:,:),intent(inout) :: A
end function
function correctNeighArray (B,d,e,f)
character :: correctNeighArray
integer, intent(in) :: d,e,f
character, dimension(:,:,:),intent(inout) :: B
end function
end interface
Мне кажется, это не лучший выбор.
Я просмотрел подпрограммы, но я не очень уверен, что это правильное решение. То, что я делаю, относительно просто, и мне нужно передать аргументы в подпрограмму, но все подпрограммы, которые я видел, являются: а) сложными (т.е. Слишком сложными для функции), и б) не принимают аргументы, они ведут себя так, как будто они манипулируют переменными без их передачи им.
Я действительно не изучал модули должным образом, но из того, что я видел, это не то, что нужно использовать.
Что мне следует использовать, когда и как мне лучше всего это делать?
Ответы
Ответ 1
Модули всегда правильные, чтобы использовать; -)
Если у вас очень простая программа F90, вы можете включить функции и подпрограммы в блок "содержит":
program simple
implicit none
integer :: x, y
x = ...
y = myfunc(x)
contains
function myfunc(x) result(y)
implicit none
integer, intent(in) :: x
integer :: y
...
end function myfunc
end program
Тогда интерфейс функций/подпрограмм будет известен в программе и не обязательно должен быть определен в интерфейсном блоке.
Для более сложных программ вы должны хранить все функции/подпрограммы в модулях и загружать их, когда это необходимо. Поэтому вам не нужно определять интерфейсы:
module mymod
implicit none
private
public :: myfunc
contains
function myfunc(x) result(y)
implicit none
integer, intent(in) :: x
integer :: y
...
end function myfunc
end module mymod
program advanced
use mymod, only: myfunc
implicit none
integer :: x, y
x = ...
y = myfunc(x)
end program advanced
Модуль и программа могут (фактически должны) быть в отдельных файлах, но модуль должен быть скомпилирован перед реальной программой.
Ответ 2
Второе и продолжение того, что уже было сказано. Лучше поместить свои процедуры (подпрограммы и функции) в модули и "использовать" их, потому что они получают автоматическую согласованность интерфейсов с минимальными усилиями. У других способов есть недостатки. Если вы определяете интерфейс с блоком интерфейса, то у вас есть три вещи для поддержки вместо двух: интерфейс, сама процедура и вызов. Если вы вносите изменения, то все три должны быть изменены, чтобы быть последовательными. Если вы используете модуль, только два должны быть изменены. Причиной использования интерфейсного блока является отсутствие доступа к исходному коду (например, предварительно скомпилированная библиотека) или исходный код на другом языке (например, вы используете код C через привязку ISO C).
Недостатком подхода "содержит" является то, что содержащиеся процедуры наследуют все локальные переменные родительской программы... которые не очень модулярны и могут быть очень запутанными, если вы забудете эту "функцию".
Ответ 3
ответы alexurba и MSB правильны и полезны, как обычно; позвольте мне просто немного подробнее описать одну точку - если модули - это путь (и они есть), что такое интерфейсы вообще?
Для функций и подпрограмм в модулях все, что use
, этот модуль может автоматически видеть эти интерфейсы; интерфейсы генерируются при компиляции модуля (эта информация, среди прочего, входит в файл .mod, сгенерированный при компиляции модуля). Поэтому вам не нужно писать это самостоятельно. Аналогично, когда вы используете подпрограмму CONTAIN
ed (которая, соглашаясь с MSB, я нахожу более запутанной, а затем полезной), их гораздо лучше воспринимают как закрытия или вложенные подпрограммы, чем внешние подпрограммы), основная программа уже может "видеть" интерфейс явно, и вам не нужно его выписывать.
Блоки интерфейса предназначены для тех случаев, когда вы не можете этого сделать - когда компилятор не может создать явный интерфейс для вас или когда вы хотите что-то отличное от того, что дано. Одним из примеров является использование совместимости C-Fortran в Fortran 2003. В этом случае код Fortran связывается с некоторой библиотекой C (скажем) и не имеет возможности генерировать исправьте fortran-интерфейс для процедуры C для вас - вам нужно сделать это самостоятельно, написав свой собственный блок интерфейса.
Еще один пример: когда вы уже знаете интерфейсы для подпрограмм, но когда вы хотите создать новый интерфейс для "скрытия" подпрограмм позади - например, когда у вас есть одна подпрограмма, которая работает (скажем) целые числа, и один - на реалах, и вы хотите иметь возможность вызывать одно и то же имя подпрограммы и разрешать компилятору сортировать его на основе аргументов. Такие конструкции называются универсальными подпрограммами и существуют со времен Fortran 90. В этом случае вы явно создаете интерфейс для этой новой универсальной подпрограммы и указываете интерфейсы на "реальных" подпрограмм в этом интерфейсном блоке.