我与FORTRAN合作很多,但我从来没有以正确的方式编写源代码的正式指令。我目前使用模块来存储全局变量,但我知道你也可以使用它们来存储子程序和函数。我使用的代码有很多子程序,因为它们非常庞大和复杂。所有功能和子程序都应该在模块中吗?如果是这样,为什么?
答案 0 :(得分:20)
一般来说,第一个问题的答案是是,我马上回答你的第二个问题。首先要注意的是,这是对一般问题的一般回答,并且围绕SO Fortran问题的明亮火花可能会提出模块不适用的特殊情况。我事先反驳说,这个答案是针对模块的新手。一旦您不再是新手,您就可以制定自己的问题答案。
模块对程序员最有用,可以帮助组织和构建程序或程序套件。它们提供了一种机制,用于封装用户定义类型的定义以及对这些类型进行操作的函数/子例程。在Fortran 90和95中,这种封装在某种程度上是临时性的,因为它依赖于程序员关于如何将程序分解为部分的想法。随着Fortran 2003中面向对象设施的引入,现在有更清晰的“规则”来识别每个模块中的元素。
例如,你可以设想一个模块用于理性算术的类型和程序。通过在一个模块中保留实现您的好主意的所有代码,您可以隐藏程序其他部分的实现(不需要知道详细信息)并仅公开您希望公开的那些部分(查看{{1 }和PRIVATE
关键字)。您可以立即看到将代码组织到模块中的另一个好处;在新程序中PUBLIC
合理的算术模块比从大型源文件中删除和传递代码到另一个大型源文件要容易得多。当你想要进行理性算术时,你需要在一个模块中处理代码,而不是在遍布文件的代码中进行处理。
模块还允许您管理名称冲突。例如,您的有理算术模块可能会定义一个名为USE
的操作,您可能还有一个多精度整数算术模块,它定义了一个名为add
的操作。如果您尝试在程序(或其他模块)中add
这两个模块,则编译器将警告(可能引发错误)在使用模块的范围内定义相同的名称两次。使用关联模块实体时,可以使用重命名。您还可以使用USE
子句仅导入用户需要的模块实体。
请注意,模块ONLY
是可传递的,如果A使用B而B使用C,则您不必声明A使用C(尽管您已重命名实体或指定的USE
子句你必须确保在特定情况下什么是传递的。)
简而言之,模块是Fortran的主要机制,通过将程序分解为可管理的块来处理程序的复杂性。 Fortran 2008,当该功能由编译器实现时,也引入了ONLY
,它承诺更好地支持以这种方式处理复杂性。
模块也很有用,因为语言标准要求编译器生成模块中定义的过程的显式接口,以便在编译时对参数进行类型检查。请注意,这些接口(您从未真正看到过)被称为显式,以与隐式接口形成对比,隐式接口是未在模块内部定义的过程(或在使用它们的程序单元内SUBMODULE
ed)具有的过程。当然,您可以为此类过程编写显式接口,但在短期和长期运行中,让编译器为您执行此操作几乎总是更容易。
正如@Telgin已经指出的那样,模块也有助于增量编译。
答案 1 :(得分:6)
使用模块的一个主要好处是,您的编译器将自动对模块中use
的任何函数或子例程执行接口检查,以确保您使用适当的参数类型调用例程。关于这个主题的一篇好文章是Doctor Fortran Gets Explicit - Again!。从这篇文章:
有几种方法可以提供显式接口。最简单和最好的方法是将过程放在模块中,或使其成为调用程序或过程的CONTAINed过程。这样做的好处是不需要您两次写入信息,从而增加了在其中一个地方出错的可能性。当您具有模块过程或包含的过程时,其接口将自动对模块中的所有其他过程或父作用域中可见。假设该名称尚未声明为PRIVATE,则该接口也可用于您使用包含模块过程的模块的位置。
我建议将所有相关的例程放在同一个模块中。对我来说,模块相当于其他语言中的类。模块是一种对相关数据和例程(可以对该数据进行操作)进行分组的方法。因此,如果您的例程按逻辑分组到模块中,并为您的函数和子例程调用添加类型检查,那么模块提供了一种使代码更易于导航的方法。
答案 2 :(得分:3)
模块通过自动提供显式接口来帮助Fortran程序,如其他一些答案中所述。这允许编译器检查过程调用中的参数与过程声明之间的一致性,这会引发很多错误 - 这里有一些Stackoverflow答案显示了模块的这些好处:Computing the cross product of two vectors in Fortran 90,FORTRAN functions和Fortran segmentation fault in pointer array
答案 3 :(得分:2)
将相关函数,子例程和变量移动到它们自己的模块可以提高程序的可维护性。稍后,当您必须更新其中的一部分时,您不必通过具有数十(或数百)数千行代码的单个源文件进行挖掘,并且可以打开相关文件。
当然,您可能认为您可以使用编辑器的搜索功能来查找相关的子程序,但是在让代码独立几个月后,您可能会很快发现你无法回想起子程序的名称或它们如何组合在一起。将它们组合得很好很有帮助。
如果编译器只需要重建单个模块而不是整个程序,那么将子程序和函数移动到模块中也可以提高编译速度。