Fortran中模块、子程序和函数的正确使用方法

28

我最近学习了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
在Fortran中,使用的是'character'而不是'char'. - alexurba
3个回答

44

使用模块总是正确的做法;-)

如果您有一个非常简单的F90程序,可以将函数和子程序包含在“contains”块中:

 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
模块和程序可以(实际上应该)放在不同的文件中,但是模块必须在实际程序之前被编译。

1
在你的顶部代码块中,你没有开始一个子例程或任何称为“mysub”的东西,但是你却关闭了它。这是允许的吗?你在第二个编码块中也做了同样的事情。这些应该是“end function myfunc”行吗?所以我可以将我的函数移到一个新模块中,然后它应该像那样工作,并且我可以调用这些函数,就好像它们在主程序中一样?我想我知道这是如何工作的。非常感谢。 - AncientSwordRage
1
函数“myfunc”应该以“end function myfunc”来结束 - 已修复。 - M. S. B.
如果包含这些过程的模块也有“implicit none”,那么每个包含过程中是否需要“implicit none”? - jvriesem

21

赞同并扩展已经说过的话。将您的过程(子例程和函数)放入模块并“使用”它们会更好,因为这样可以在很少的努力下获得接口的自动一致性检查。其他方法有缺点。如果您使用接口块定义接口,则需要维护三个东西而不是两个:接口、过程本身和调用。如果进行更改,则必须修改所有三个以保持一致。如果您使用模块,则只需更改两个。使用接口块的原因是如果您无法访问源代码(例如,预编译库)或源代码使用另一种语言(例如,您正在通过ISO C绑定使用C代码)。

“包含”方法的缺点是包含的过程继承了父程序的所有局部变量... 这不太模块化,如果您忘记了这个“功能”,可能会非常令人困惑。


19

alexurba和MSB的回答像往常一样正确和有用;让我在一个问题上详细解释一下 - 如果模块是最好的选择(确实如此),那么接口到底有什么用呢?

对于模块中的函数和子程序,使用该模块的任何内容都可以自动看到这些接口;接口在编译模块时生成(其中包含该信息以及其他信息,这些信息会在编译模块时生成.mod文件)。因此,您不需要自己编写它。同样,当您使用CONTAINed子程序(与MSB的意见相符,我发现它们更加混乱而不是有帮助 - 它们更好地被视为closuresnested subroutines而不是外部子程序)时,主程序已经可以明确地“看到”接口,因此不需要您编写它。

接口块用于当你无法这样做时 - 当编译器无法为你生成显式接口,或者当你想要与现有接口不同的东西时。一个例子是在 Fortran 2003 中使用C-Fortran互操作性时。在这种情况下,Fortran 代码链接到某个 C 库(比如说),并且无法为你生成正确的 Fortran 接口来调用 C 例程 - 你必须自己编写接口块。

另一个例子是当你已经知道子程序的接口,但是当你想要创建一个新的接口来"隐藏"这些子程序时,例如,当你有一个操作整数类型和一个操作实数类型的例程,并且希望能够在两种类型上调用相同的例程名称,并让编译器基于参数进行排序。这种构造被称为通用例程,自 Fortran 90 以来就一直存在。在这种情况下,你需要显式地创建一个接口来支持这个新的通用例程,并在接口块中列出“真正”的例程接口。


1
+1 当然接口很有用,我尽可能经常使用通用程序。 - alexurba

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接