6

我经常使用 FORTRAN,但我从未接受过以正确方式编写源代码的正式指导。我目前使用模块来存储全局变量,但我知道您也可以使用它们来存储子例程和函数。我使用的代码有很多子例程,因为它们非常庞大且复杂。所有函数和子程序都应该在模块中吗?如果是这样,为什么?

4

4 回答 4

21

一般来说,您的第一个问题的答案是肯定的,稍后我会回答您的第二个问题。首先请注意,这是对一般问题的一般答案,围绕 SO Fortran 问题的明亮火花很可能会提出模块不适用的特殊情况。我提前反驳说,这个答案是针对模块的新手。一旦您不再是新手,您就可以制定自己的问题答案。

模块对程序员最有用,可帮助组织和构建程序或程序套件。它们提供了一种机制来封装用户定义类型的定义以及对这些类型进行操作的函数/子例程。在 Fortran 90 和 95 中,这种封装有点特别,因为它依赖于程序员关于如何将程序分解为多个部分的想法。随着 Fortran 2003 中面向对象工具的引入,现在有了更清晰的“规则”来识别每个模块中属于哪些元素。

例如,您可以设想一个用于有理算术的类型和过程的模块。通过将所有实现您伟大想法的代码保留在一个模块中,您可以隐藏程序其他部分的实现(不需要知道细节)并只公开您希望公开的部分(查看PRIVATEPUBLIC关键字) . 您可以立即看到将代码组织成模块的另一个优势;USE在一个新程序中你的理性算术模块比将代码从你的巨型源文件剪切并粘贴到另一个巨型源文件中要容易得多。当你想处理你的有理算术时,你在一个模块中处理代码,而不是在你的文件中散布的代码。

模块还允许您管理名称冲突。例如,您的有理算术模块可能定义了一个名为 的运算add,并且您可能还有一个多精度整数算术模块,该模块定义了一个名为 的运算add。如果您尝试USE在一个程序(或另一个模块)中同时使用这两个模块,那么编译器将警告(可能引发错误)相同的名称在使用模块的范围内定义了两次。使用关联模块实体时可以使用重命名。您还可以使用ONLY子句仅导入用户需要的那些模块实体。

请注意,模块USE是可传递的,如果 A 使用 B 并且 B 使用 C,则您不必还声明 A 使用 C(尽管如果您已重命名实体或指定ONLY子句,则必须确保在特定的案子)。

简而言之,模块是处理程序复杂性的主要 Fortran 机制,通过将它们分成可管理的块。Fortran 2008,当该功能由编译器实现时,SUBMODULE也引入了 s,它承诺更好地支持以这种方式处理复杂性。

模块也很有用,因为语言标准要求编译器为模块中定义的过程生成显式接口,以便在编译时对参数进行类型检查。请注意,这些接口(您从未真正看到)被称为显式接口,以与隐式接口形成对比,隐式接口是未在模块内部定义(或CONTAIN在使用它们的程序单元中编辑)具有的过程。当然,您可以为此类过程编写显式接口,但在短期和长期运行中,让编译器为您完成几乎总是更容易。

正如@Telgin 已经指出的那样,模块也有助于增量编译。

于 2012-08-14T13:37:28.960 回答
6

使用模块的主要好处之一是您的编译器将自动对use来自模块的任何函数或子例程执行接口检查,以确保您使用适当的参数类型调用例程。关于这个主题的一篇好文章是Fortran 博士变得明确 - 再次!. 从这篇文章:

有几种方法可以提供显式接口。最简单和最好的方法是将过程放在一个模块中,或者使其成为调用程序或过程的 CONTAINed 过程。这样做的好处是不需要您将信息写两次,从而增加了在其中一个地方出错的机会。当您有一个模块过程或包含的过程时,它的接口自动对模块或父范围内的所有其他内容可见。假设名称尚未声明为 PRIVATE,则该接口也可用于您使用包含模块过程的模块的地方。

我建议将所有相关例程放在同一个模块中。对我来说,模块相当于其他语言中的类。模块是一种对相关数据和例程(可以对这些数据进行操作)进行分组的方法。因此,如果您的例程按逻辑分组到模块中,并为您的函数和子例程调用添加类型检查,则模块提供了一种使您的代码更易于导航的方法。

于 2012-08-14T13:37:44.570 回答
3

模块通过自动提供显式接口来帮助 Fortran 程序,正如其他一些答案中已经描述的那样。这允许编译器检查过程调用和过程声明中的参数之间的一致性,这会发现很多错误——这里有一些 Stackoverflow 答案,显示了模块的这种好处:Computing the cross product of two vectors in Fortran 90 , FORTRAN指针数组中的函数Fortran 分段错误

于 2012-08-14T15:13:01.313 回答
2

将相关的函数、子程序和变量移到它们自己的模块中可以提高程序的可维护性。稍后当你必须更新它的一部分时,你不必去挖掘具有数万(或数百)数千行代码的单个源文件,只需打开相关文件即可。

当然,您可能认为您可以只使用编辑器的搜索功能来查找相关的子程序,但是在让代码单独放置几个月后,您可能很快就会发现您记不起名字了子程序或它们如何组合在一起。将它们分组很好地对此有很大帮助。

如果编译器只需重建单个模块而不是整个程序,则将子例程和函数移动到模块中也可以提高编译速度。

于 2012-08-14T13:19:53.690 回答