在Fortran 90中模拟命名空间

14
Fortran 90最棘手的问题之一是缺乏命名空间。在Pete的先前问题“如何使用Fortran 90模块数据”中已经讨论了USE行为类似于Python中的“from module import *”的主要问题:在导入模块的范围内,所有声明为公共的内容都会原样导入。没有前缀。这使得在阅读代码时非常难以理解给定标识符来自哪里,以及是否仍在使用给定模块。
在我上面链接的问题中讨论的一个可能的解决方案是使用ONLY关键字来限制导入的标识符并记录它们来自哪里,尽管当模块非常大时,这非常费时。保持模块小,并始终使用USE:ONLY是解决Fortran 9X中缺乏命名空间和合格前缀的潜在良策。
还有其他(不一定更好的)解决策略吗?Fortran 2k3标准是否对命名空间支持有任何说明?

一个晚期的评论,似乎 Fortran 03 可以在面向对象编程中使用 % https://fortranwiki.org/fortran/show/Object-oriented+programming,有点类似于命名空间 :: - AlphaF20
4个回答

6

我之前只有几年Fortran编程经验(直到一年前才开始接触Python),所以一段时间内我并不知道什么是命名空间。我猜我学会了仅仅跟踪所有导入的内容,如High Performance Mark所说,尽可能地使用,并且只用需要的部分(繁琐)。

另一个我想到的模拟命名空间的方法是将模块中的所有东西都声明为派生类型的组成部分。 Fortran不允许您将模块命名为命名空间,但在模块名称前加上module_可能足够直观:

MODULE module_constants
IMPLICIT NONE

TYPE constants_namespace
  REAL :: pi=3.14159
  REAL ::  e=2.71828
ENDTYPE

TYPE(constants_namespace) :: constants

ENDMODULE module_constants


PROGRAM namespaces
USE module_constants
IMPLICIT NONE

WRITE(*,*)constants%pi
WRITE(*,*)constants%e

ENDPROGRAM namespaces

注意:这适用于变量,但不适用于函数。Fortran90不支持子程序指针的事实令人震惊,至少可以这么说。只要有了这个小功能,就可以解锁大量的可能性。 - Stefano Borini
没错。对于子程序,你仍然需要使用USE..ONLY,并且可以选择重命名,就像kemiisto建议的那样。我想这是下一个Fortran标准发布时应该考虑的事情。 - milancurcic
1
在Fortran 2003中(诚然不是在Fortran 90中),类型绑定过程的概念可以用于在函数和子程序前缀中添加类型名称,就像上面对变量所做的那样。但我不知道这是否会影响性能。 - norio

6

对我来说,这是与模块相关的最令人恼火的Fortran功能。唯一的解决方案是为过程、变量、常量等添加公共前缀,以避免命名空间冲突。

可以在模块内部为所有实体(似乎更适合所有公共实体)添加前缀:

module constants

  implicit none

  real, parameter :: constants_pi = 3.14
  real, parameter :: constants_e = 2.71828183

end module constants

缺点是模块内部的代码冗长。作为替代方案,可以使用命名空间前缀包装器模块,如此处所建议的。

module constants_internal

  implicit none

  real, parameter :: pi = 3.14
  real, parameter :: e = 2.71828183

end module constants_internal

module constants

  use constants_internal, only: &
    constants_pi => pi, &
    constants_e => e

end module constants

最后是对你,Stefano所建议的小修改。

即使我们接受冗长的情况,Fortran不区分大小写的语言事实迫使我们在实体名称中使用相同的分隔符(_)。除非我们使用强命名规则(例如,模块名称只有一个单词),否则很难区分模块名称(作为前缀)和实体名称。


1
如果我理解正确,你的第二段意思是例如physics_const_earth_radius并不能确定physics是模块名还是physics_const。你可以使用双下划线:physical_constants__earth_radius - Matthias 009
1
@Matthias009 当然可以。但为了视觉上的清晰度,我更倾向于使用 _mod_ 这样的东西。 - Wildcat
如果模块中有超过255个实体(255是F2003中连续行的最大数量),那么这将如何工作?可以将“use”语句分成几个吗? - Jellby

3
Fortran 2003引入了新的ASSOCIATE结构,并且不要忘记重命名USE关联实体的可能性。但我认为这两者都没有比Fortran 90更接近提供良好的命名空间模拟,只是(稍微)更好的解决方法。
像您链接的问题的一些回答者一样,我倾向于认为具有许多标识符的模块应该分成较小的模块(或等待Fortran 2008并使用子模块),而且现在我几乎总是为USE语句指定ONLY子句(带有重命名)。
我不能说我非常想念命名空间,但我从来没有真正拥有过它们。

1
没有其他人提交这个建议作为答案(尽管有人在一个答案的评论中提到了)。因此,我希望以此来帮助其他人,您可以通过以下方式模拟名称空间,我希望编译器不会因此产生明显的性能损失(如果是这样的话,所有面向对象的Fortran编程都会受到影响)。我在我的生产代码中使用这个模式。对我来说唯一真正的缺点是缺乏派生类型包含的参数变量,但是我在下面也提供了一个选项。
Module Math_M

    IMPLICIT NONE
    
    PRIVATE

    public :: Math

    Type Math_T
        real :: pi=3.14159
    contains
        procedure, nopass :: e => math_e
        procedure :: calcAreaOfCircle => math_calcAreaOfCircle
    End Type

    Type(Math_T) :: Math
    real, parameter :: m_e = 2.71828

    contains

    function math_e() result(e)
        real :: e
        e = m_e
    end function math_e

    function math_calcAreaOfCircle(this, r) result(a)

        class(Math_T), intent(in) :: this
        real, intent(in) :: r

        real :: a

        a = this%pi * r**2.0
    end function math_calcAreaOfCircle
End Module Math_M

使用方法

Program Main

    use Math_M

    IMPLICIT NONE

    print *, Math%pi
    print *, Math%e()
    print *, Math%calcAreaOfCircle(2.0)


End Program Main

个人而言,我更喜欢在模块变量中使用$而不是_,但不是所有编译器都支持这种写法,需要使用编译器标志。希望这能对未来的某些人有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接