Fortran 中模块、子例程和函数的正确使用
我最近在向 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)不接受参数。它们的行为就好像它们在没有将变量传递给它们的情况下操纵变量一样。
我还没有真正正确地研究过模块,但从我所看到的来看,这不是正确的使用方式。
我应该何时使用哪一个,以及如何最好地使用它?
I've recently learnt about interface blocks when adding a function to my Fortran program. Everything works nice and neatly, but now I want to add a second function into the interface block.
Here is my interface block:
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
It appears to me that this may not be the best option.
I've looked into subroutines, but I'm not very confident that it's the right solution. What I'm doing is relatively simple, and I need to pass arguments to the subroutine, but all the subroutines I've seen are a) complicated (i.e. too complicated for a function), and b) don't take arguments. They behave as though they manipulate variables without them being passed to them.
I've not really looked into modules properly, but from what I've seen it's not the right thing to use.
Which should I use when, and how do I go about it best?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
模块始终是正确的选择;-)
如果您有一个非常简单的 F90 程序,您可以在“包含”块中包含函数和子例程:
那么函数/子例程的接口将在程序中已知,并且不会需要在接口块中定义。
对于更复杂的程序,您应该将所有函数/子例程保留在模块中,并在需要时加载它们。因此,您也不需要定义接口:
模块和程序可以(实际上应该)位于单独的文件中,但模块必须在实际程序之前编译。
Modules are always the right thing to use ;-)
If you have a very simple F90 program you can include functions and subroutines in the 'contains' block:
Then the interface of the functions/subroutines will be known in the program and don't need to be defined in an interface block.
For more complex programs you should keep all functions/subroutines in modules and load them when required. So you don't need to define interfaces, either:
The module and the program can (actually should) be in separate files, but the module has to be compiled before the actual program.
支持并扩展已经说过的内容。最好将您的过程(子例程和函数)放入模块中并“使用”它们,因为您可以轻松地对接口进行自动一致性检查。其他方法也有缺点。如果您使用接口块定义接口,那么您需要维护三件事,而不是两件事:接口、过程本身和调用。如果您进行更改,则所有三个都必须修改才能保持一致。如果您使用一个模块,则只需更改两个。使用接口块的一个原因是,如果您无法访问源代码(例如,预编译库)或源代码采用另一种语言(例如,您通过 ISO C 绑定使用 C 代码)。
“包含”方法的缺点是包含的过程继承父程序的所有局部变量......这不是很模块化,如果您忘记这个“功能”,可能会非常混乱。
Seconding and extending what has already been said. It is better to put your procedures (subroutines and functions) into modules and "use" them because them you get automatic consistency checking of the interfaces with little effort. The other ways have drawbacks. If you define the interface with an interface block, then you have three things to maintain instead of two: the interface, the procedure itself and the call. If you make a change, then all three have to be modified to be consistent. If you use a module, only two have to be changed. A reason to use an interface block is if you don't have access to the source code (e.g., pre-compiled library) or the source code is in another language (e.g., you are using C code via the ISO C Binding).
The drawback to the "contains" approach is that contained procedures inherit all of the local variables of the parent program ... which is not very modular and can be very confusing if you forget this "feature".
alexurba 和 MSB 的答案一如既往地正确且有用;让我在一点上充实一点细节——如果模块是可行的方法(而且确实如此),那么接口到底有什么用呢?
对于模块中的函数和子例程,任何
使用
该模块的东西都可以自动看到这些接口;接口是在编译模块时生成的(该信息除其他外,会进入编译模块时生成的 .mod 文件)。所以你不需要自己写。类似地,当您使用CONTAIN
ed 子程序时(与 MSB 一致,我发现它更令人困惑而不是有帮助 - 它们更好地被认为是 闭包 或 嵌套子例程而不是外部子例程),主程序已经可以显式地“看到”接口,并且不需要您为其编写它。接口块适用于您无法执行此操作的情况 - 当编译器无法为您生成显式接口时,或者当您想要与给定的接口不同的东西时。一个例子是使用 C 时-Fortran 2003 中的 Fortran 互操作性。在这种情况下,Fortran 代码链接到某些 C 库(例如),并且无法生成为您提供正确的 C 例程的 Fortran 接口——您必须自己编写自己的接口块。
另一个例子是,当您已经知道子例程的接口时,但是当您想要创建一个新接口来“隐藏”后面的子例程时 - 例如,当您有一个对(比如说)整数进行操作的例程和一个对实数进行操作的例程时,并且您希望能够在两者上调用相同的例程名称,并让编译器根据参数对其进行排序。此类构造称为 泛型例程,自 Fortran 90 以来就已存在。在这种情况下,您可以显式地为这个新泛型创建一个接口例程,并列出该接口块中“真实”例程的接口。
alexurba's and MSB's answers are correct and useful as usual; let me just flesh out a little more detail on one point - if modules are the way to go (and they are), what are interfaces for at all?
For functions and subroutines in modules, anything that
use
s that module can automatically see those interfaces; the interfaces are generated when the module is compiled (that information, amongst other things, goes into the .mod file that's generated when you compile a module). So you don't need to write it yourself. Similarly, when you use aCONTAIN
ed subprogram (which, agreeing with MSB, I find more confusing then helpful - they're much better thought of as closures or nested subroutines than external subroutines), the main program can already 'see' the interface explicitly and it doesn't need you to write it out for it.Interface blocks are for when you can't do this - when the compiler isn't able to generate the explicit interface for you, or when you want something different than what is given. One example is when using C-Fortran interoperability in Fortran 2003. In that case, the Fortran code is linking against some C library (say) and has no way of generating a correct fortran interface to the C routine for you -- you have to do it yourself, by writing your own interface block.
Another example is when you do already know the interfaces to subroutines, but when you want to create a new interface to "hide" the subroutines behind - for instance, when you have one routine that operates on (say) integers, and one on reals, and you want to be able to call the same routine name on either and let the compiler sort it out based on the arguments. Such constructs are called generic routines and have been around since Fortran 90. In that case, you create an interface explicitly to this new generic routine, and list the interfaces to the "real" routines within that interface block.