【问题标题】:Correct use of modules, subroutines and functions in Fortran正确使用 Fortran 中的模块、子程序和函数
【发布时间】:2012-01-14 19:37:41
【问题描述】:

我最近在向我的 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

在我看来,这可能不是最好的选择。

我研究过子程序,但我不太确定这是正确的解决方案。我正在做的事情相对简单,我需要将参数传递给子例程,但是我见过的所有子例程a)复杂(即对于函数来说太复杂),b)不带参数。它们的行为就好像它们在不将变量传递给它们的情况下操纵变量一样。

我并没有真正正确地研究模块,但据我所见,它不是正确的使用方法。

我应该在什么时候使用哪个,以及如何最好地使用它?

【问题讨论】:

  • 在 Fortran 中是 'character' 而不是 'char'

标签: function module fortran fortran90 subroutine


【解决方案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

模块和程序可以(实际上应该)在单独的文件中,但模块必须在实际程序之前编译。

【讨论】:

  • 在您的顶部代码块中,您不会启动子程序或任何称为mysub 的程序,但您会关闭它。这是允许的吗?您在第二个编码块中执行相同的操作。这些应该是end function myfunc 行吗?所以我可以将我的函数转移到一个新模块中,它应该像那样工作,我可以调用这些函数,就好像它们在主程序中一样?我想我明白这是如何工作的。非常感谢。
  • "function myfunc" 应该以 "end function myfunc" 结束 -- 已修复。
  • 如果包含这些过程的模块也有implicit none,那么每个包含的过程中是否需要implicit none
【解决方案2】:

延续和扩展已经说过的内容。最好将您的过程(子例程和函数)放入模块中并“使用”它们,因为它们可以轻松地自动检查接口的一致性。其他方式都有缺点。如果使用接口块定义接口,则需要维护三件事而不是两件事:接口、过程本身和调用。如果您进行更改,则必须修改所有三个以保持一致。如果您使用一个模块,则只需更改两个。使用接口块的一个原因是如果您无权访问源代码(例如,预编译库)或源代码是另一种语言(例如,您正在通过 ISO C 绑定使用 C 代码)。

“包含”方法的缺点是,包含的过程继承了父程序的所有局部变量……这不是非常模块化,如果您忘记了这个“特性”,可能会非常混乱。

【讨论】:

    【解决方案3】:

    alexurba 和 MSB 的答案像往常一样正确且有用;让我在某一点上更详细地说明一下 - 如果模块是要走的路(而且确实如此),那么接口到底有什么用?

    对于模块中的函数和子例程,uses 那个模块可以自动看到那些接口的任何东西;接口是在编译模块时生成的(除其他外,该信息进入编译模块时生成的 .mod 文件)。所以你不需要自己写。同样,当您使用 CONTAINed 子程序时(与 MSB 一致,我发现它更令人困惑,但更有帮助 - 与外部子程序相比,它们被认为是 closuresnested subroutines 更好),主程序已经可以明确地“看到”接口,不需要你为它写出来。

    接口块用于当您不能执行此操作时 - 当编译器无法为您生成显式接口时,或者当您想要与给定不同的东西时。一个例子是在 Fortran 2003 中使用 C-Fortran interoperability 时。在这种情况下,Fortran 代码链接到某个 C 库(比如说),并且无法为您生成 C 例程的正确 fortran 接口——您必须这样做自己动手,通过编写自己的接口块。

    另一个例子是当你已经知道子程序的接口,但是当你想创建一个新的接口来“隐藏”后面的子程序时——例如,当你有一个对(比如说)整数进行操作的例程时,并且一个在实数上,您希望能够在其中一个上调用相同的例程名称,并让编译器根据参数对其进行排序。此类构造称为generic routines,自 Fortran 90 以来一直存在。在这种情况下,您可以为这个新的通用例程显式创建一个接口,并在该接口块中列出与“真实”例程的接口。

    【讨论】:

    • +1 当然接口很有用,我倾向于尽可能多地使用通用过程。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-07
    • 1970-01-01
    相关资源
    最近更新 更多