我经常使用FORTRAN,但从未接受过正式的源代码编写指导。我目前使用模块来存储全局变量,但我知道你也可以使用它们来存储子程序和函数。我处理的代码有许多子程序,因为它们非常庞大和复杂。所有函数和子程序都应该在模块中吗?如果是,为什么?
我经常使用FORTRAN,但从未接受过正式的源代码编写指导。我目前使用模块来存储全局变量,但我知道你也可以使用它们来存储子程序和函数。我处理的代码有许多子程序,因为它们非常庞大和复杂。所有函数和子程序都应该在模块中吗?如果是,为什么?
一般来说,对于您的第一个问题,答案是是,关于第二个问题,我马上会给出答案。请注意,这是对于一般问题的一般回答,而那些精通SO Fortran问题的人可能会提出特殊情况,在这些情况下模块是不适用的。我预先反驳一下,这个答案是针对新手学习模块使用的。一旦您不再是新手,您可以自己回答您的问题。
对于程序员而言,模块作为组织和结构化程序或一组程序的工具非常有用。它们提供了一种机制,用于封装用户定义类型的定义以及操作这些类型的函数/子程序的定义。在Fortran 90和95中,这种封装有点临时性,因为它依赖于程序员如何将程序分解成部分的想法。随着Fortran 2003中面向对象设施的引入,现在有更清晰的“规则”来确定哪些元素属于每个模块。
例如,您可以构思一个用于有理算术的类型和过程的模块。通过将实现您伟大想法的所有代码保存在一个模块中,您可以将其从程序的其他部分(不需要知道细节的部分)隐藏,并仅公开您希望公开的那些部分(请查看PRIVATE
和PUBLIC
关键字)。你可以立即看到将代码组织成模块的另一个好处;在新程序中使用您的有理算术模块比将代码从您的大型源文件中剪切粘贴到另一个大型源文件中要容易得多。当您想要处理您的有理算术时,您只需处理一个模块中的代码,而不是处理分散在文件周围的代码。
模块也允许您管理名称冲突。例如,您的有理算术模块可能会定义一个名为add
的操作,而您还可能有一个定义了名为add
的操作的多精度整数算术模块。如果您尝试在程序(或另一个模块)中使用这两个模块,则编译器将警告(可能引发错误),因为在使用模块的范围内定义了相同的名称两次。当使用关联模块实体时,可以使用重命名。您也可以使用ONLY
子句仅导入用户需要的那些模块实体。
请注意,模块USE
是传递的,如果A使用B且B使用C,则无需声明A也使用C(尽管如果您已重命名实体或指定了ONLY
子句,则必须确保在特定情况下它是传递的)。
简而言之,通过将程序分解成可管理的块,模块是处理程序复杂性的主要Fortran机制。当编译器实现该功能时,Fortran 2008引入了SUBMODULE
,这些子模块承诺以更好的方式支持处理复杂性。
模块也很有用,因为语言标准要求编译器为在模块中定义的过程生成显式接口,以便在编译时进行参数类型检查。请注意,这些接口(您实际上从未看到)称为显式接口,以与没有在模块内定义(或CONTAIN
在使用它们的程序单元内)的过程的隐式接口形成对比。当然,您可以为这些过程编写显式接口,但短期和长期来看,让编译器为您完成通常更容易。
正如@Telgin已经指出的那样,模块还有助于递增编译。
.o
和.mod
文件,并且它们的时间戳正确,那么我的Makefile就不会重新编译它们。不过,我知道当修改模块但其接口保持不变时,会发生Fortran编译级联。 - High Performance Mark.o
文件,那也是可以的(与模块无关)。模块的问题在于它们强制执行一种编译顺序,而否则是不存在的,并且编译一个模块会生成2个文件,这是一个(可悲的)make
似乎完全忘记的情况。但我认为,通过使用模块获得的功能和安全性所付出的代价很小。 - mgilsonuse
的任何函数或子程序执行接口检查,以确保您使用适当的参数类型调用例程。关于这个主题的一个很好的文章是Doctor Fortran Gets Explicit - Again!。来自这篇文章的内容如下:
我建议将所有相关例程放在同一个模块中。对我来说,模块相当于其他语言中的类。模块是一种将相关数据和例程(可能操作该数据)分组的方法。因此,如果您的例程在逻辑上分组到模块中,则模块提供了一种使代码更易于导航的方法,并为函数和子例程调用添加类型检查。提供显式接口的几种方法。最简单和最好的方法是将过程放入模块中,或者将其作为调用程序或过程的CONTAINed过程。这样做的好处是不需要在两个地方写信息,从而增加出现错误的机会。当您拥有模块过程或包含过程时,它的接口会自动对模块或父级范围中的其他所有内容可见。假设该名称未声明为PRIVATE,则该接口也可用于使用包含模块过程的模块的地方。
模块有助于Fortran程序,通过自动提供显式接口来避免程序中的一些其他答案已经描述过的问题。这使得编译器能够检查过程调用中的参数与过程声明之间的一致性,从而捕获许多错误 - 下面是一些Stackoverflow答案,展示了模块的这些好处:计算Fortran 90中两个向量的叉积、FORTRAN函数和指针数组中的Fortran分段故障