Правильное использование модулей, подпрограмм и функций в Fortran - PullRequest
28 голосов
/ 07 декабря 2011

Я недавно узнал о блоках tinterface при добавлении функции в мою программу на Фортране. Все работает красиво и аккуратно, но теперь я хочу добавить в блок интерфейса вторую функцию.

Вот мой блок интерфейса:

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

Мне кажется, это не самый лучший вариант.

Я изучил подпрограммы, но я не очень уверен, что это правильное решение. То, что я делаю, относительно просто, и мне нужно передать аргументы подпрограмме, но все подпрограммы, которые я видел, являются а) сложными (т.е. слишком сложными для функции) и б) не принимают аргументов, ведут себя так, как будто они манипулируют переменными без их передачи им.

Я на самом деле не изучал модули должным образом, но из того, что я видел, это не то, что нужно использовать.

Что мне следует использовать, когда и как мне лучше всего это сделать?

Ответы [ 3 ]

40 голосов
/ 07 декабря 2011

Модули всегда правильные для использования; -)

Если у вас очень простая программа 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

Модуль и программа могут (на самом деле должны) находиться в отдельных файлах, но модуль должен быть скомпилирован перед самой программой.

17 голосов
/ 07 декабря 2011

Отделение и расширение того, что уже было сказано. Лучше поместить ваши процедуры (подпрограммы и функции) в модули и «использовать» их, потому что с их помощью вы получаете автоматическую проверку согласованности интерфейсов без особых усилий. У других способов есть недостатки. Если вы определяете интерфейс с помощью блока интерфейса, то вам нужно поддерживать три вещи вместо двух: интерфейс, саму процедуру и вызов. Если вы вносите изменения, то все три должны быть изменены, чтобы быть последовательными. Если вы используете модуль, только два должны быть изменены. Причиной использования интерфейсного блока является то, что у вас нет доступа к исходному коду (например, предварительно скомпилированной библиотеке) или исходный код написан на другом языке (например, вы используете код C через привязку ISO C).

Недостаток подхода «содержит» заключается в том, что содержащиеся процедуры наследуют все локальные переменные родительской программы ... что не очень модульно и может привести к путанице, если вы забудете эту «функцию».

16 голосов
/ 07 декабря 2011

ответы Александры и MSB, как обычно, правильные и полезные; позвольте мне немного подробнее остановиться на одном вопросе - если модули - это путь (и они есть), для чего вообще нужны интерфейсы?

Для функций и подпрограмм в модулях, все, что use s, модуль может автоматически видеть эти интерфейсы; интерфейсы генерируются при компиляции модуля (эта информация, помимо прочего, попадает в файл .mod, который генерируется при компиляции модуля). Так что вам не нужно писать это самостоятельно. Точно так же, когда вы используете подпрограмму CONTAIN ed (что, соглашаясь с MSB, я нахожу более запутанным, чем полезным - их гораздо лучше воспринимать как замыкания или вложенных подпрограмм , чем внешние подпрограммы), основная программа уже может «видеть» интерфейс в явном виде, и вам не нужно выписывать его для него.

Интерфейсные блоки предназначены для случаев, когда вы не можете сделать это - когда компилятор не может сгенерировать явный интерфейс для вас, или когда вы хотите что-то отличное от того, что дано. Одним из примеров является использование совместимости с C-Fortran в Fortran 2003. В этом случае код Fortran связывается с некоторой библиотекой C (скажем) и не имеет возможности генерировать правильный интерфейс Fortran для подпрограммы C для Вы - вы должны сделать это самостоятельно, написав свой собственный интерфейсный блок.

Другой пример, когда вы уже знаете интерфейсы для подпрограмм, но когда вы хотите создать новый интерфейс, чтобы «скрыть» подпрограммы позади - например, когда у вас есть одна подпрограмма, которая работает, скажем, с целыми числами, и один на самом деле, и вы хотите иметь возможность вызывать одно и то же имя подпрограммы и позволить компилятору разобраться в этом на основе аргументов. Такие конструкции называются универсальные подпрограммы и существуют с Fortran 90. В этом случае вы явно создаете интерфейс для этой новой универсальной подпрограммы и перечисляете интерфейсы для «реальных» подпрограмм в этом блоке интерфейса.

...