gpt4 book ai didi

variables - Fortran 中的主机关联与使用关联

转载 作者:行者123 更新时间:2023-12-05 04:09:19 25 4
gpt4 key购买 nike

关于何时一个比另一个更可取,是否有任何“一般规则”?

这个问题的上下文是:我昨天问了一个关于主机协会的不同问题(link)在评论中,我被建议谨慎使用主机关联。原因是通过主机关联,很容易无意中修改变量,因为子例程可以不受限制地访问模块中声明的所有变量。

为了说明这一点,我将使用以下代码示例:

module mod
implicit none

real :: x

contains

subroutine sub(y)
use other_mod, only: a

real, intent(out) :: y

y = a + x
a = a + 1.
x = x + 1.
end subroutine sub

end module mod

ax都在sub中被修改。但是对于 x,我需要遍历所有代码才能看到这一点。通过查看 sub 的声明部分可以很容易地看出 asub 中使用(并且可能被修改)。

从这个意义上说,拥有两种模块似乎更可取:

  1. 一个或多个模块只包含变量声明(然后在需要时使用)
  2. 只包含过程和可能的参数声明但不包含变量声明的模块

这完全摆脱了变量的主机关联。

但这似乎不切实际,原因有很多:

  • 我可能有十几个子例程在一个模块中使用(和修改)相同的变量。每次都必须使用这些变量会使代码困惑,尤其是当它们很多(比如几百个)时。
  • 将变量的声明与实际使用的地方分开似乎会使代码更难理解:
    • 要么创建一个包含所有声明的巨大控制文件。如果代码很大并且使用很多变量,这可能会非常困惑。
    • 或者,为每个模块(或一组模块,如果它们依赖于相同的内容)创建一个单独的控制文件。这将使代码本身更易于理解,因为使用变量会立即显示它们的来源。但它会使代码结构复杂化,创建一个复杂得多的文件结构(以及伴随的依赖结构)。

最后,所有这些归结为:什么时候将变量声明放在使用它们的同一模块中(以便它们被主机关联使用)更明智,什么时候是将声明外包给单独的模块更明智(以便在需要时通过使用关联使用变量)?

是否有任何一般准则,还是应该根据具体情况来决定?如果是个案,那么选择一个而不是另一个的原因是什么?

最佳答案

Fortran 提供了几种创建、存储、使用和在不同“程序单元”之间传递数据的方法:主程序、外部过程和模块。1 作为您知道,每个程序单元都可以包含内部过程——通过主机关联,这些内部过程可以访问主机中包含的任何变量或过程。这通常被视为一种优势。正如@HighPerformanceMark 在他的评论中已经提到的,何时使用主机关联或使用关联的一般准则是:

use host-association when variables are only (or mainly) used by routines declared in the same module, and use use-association when you want to define variables to be used in many modules

从您的评论来看,听起来您的主程序中的大部分或所有主机变量都被每个内部过程(大约十几个子例程)访问。如果是这样的话,那么主机关联似乎是一个非常合理的选择,而且实际上没有必要显式地将参数传递给每个子例程。另一方面,如果每个子例程实际上只使用变量的一个子集,那么更明确地了解它可能是合理的。

和您一样,我通常不愿意在未在参数列表中声明的过程中使用变量。这部分是因为我喜欢 args 列表是如何 self 记录的,它帮助我推理代码以及如何在其中操作数据。在与其他工作人员协作时,或者如果我已经花了一些时间远离代码并且我对它的内存已经消退时,情况更是如此。然而,我发现几乎没有理由完全避免主机关联,只要您知道它是如何工作的并且有一个策略。

事实上,我倾向于经常使用内部程序和主机关联,尤其是对于短函数/子程序。我发现将宿主粗略地视为“对象”,将其变量视为“属性”,以及非常类似于执行工作的对象的“方法”的任何内部过程是有帮助的。当然,这是在简化事情,但这才是真正的重点。

对于更复杂的程序,我减少了“主”程序本身的主机关联数量,“主”程序本身的存在主要是为了以正确的顺序和上下文调用各种子例程。在这种情况下,我们可以利用use-association,选择直接在需要的程序单元内使用模块实体(例如过程、变量、类型、参数)他们。我们可以进一步限制仅访问那些需要使用 only: 的模块实体。这有助于提高可读性,清楚地指示数据流,而且我发现以后更新代码更直接。您知道,继承、封装等等……但是 Fortran 风格。这实际上非常好。

这是一个适用于我和我用 Fortran 处理过的中等规模项目的示例程序结构。我喜欢将我广泛使用的(静态)参数保存在一个单独的模块(或多个模块,如果根据功能分组)中。我将派生类型和类型绑定(bind)过程保存在另一个单独的模块中。如果它有用,我将某些模块实体设为 private,这样其他程序单元就无法访问它们。我想就是这样。

module params
implicit none
public !! All items public/accessible by default.
integer, parameter :: dp = kind(0.d0)
integer, parameter :: nrows = 3
real(dp), parameter :: one=1.0_dp, two=2.0_dp
...
end module params

module types
use params, only: dp, nrows
implicit none
public !! Public by default.
private :: dim2
...
integer, parameter :: dim2 = 3
...
type :: A
integer :: id
real(dp), dimension(nrows,dim2) :: data
contains
procedure, pass :: init
end type A
...
contains
subroutine init(self, ...)
...
end subroutine init
...
end module types

module utils
implicit none
private !! Private by default.
public :: workSub1, workSub2, subErr
...
integer,save :: count=0 !! Accessible only to entities in this module.
...
contains
subroutine workSub1(...)
...
end subroutine workSub1

subroutine workSub2(...)
...
end subroutine workSub2

subroutine subErr(...)
...
end subroutine subErr

end module utils


program main
!! An example program structure.
use params, only: dp
implicit none
real(dp) :: xvar, yvar, zvar
integer :: n, i
logical :: rc

call execute_work_subroutines()

contains !! Internal procs inherit all vars declared or USEd.
subroutine execute_work_subroutines()
use types, only: A
type(A) :: DataSet
!! begin
call DataSet%init(i)
do i = 1,n
call workSub1(xvar,yvar,zvar,A,i,rc)
if (rc) call subErr(rc)
call workSub2(A,rc)
if (rc) call subErr(rc)
enddo
end subroutine execute_work_subroutines
end program main

1还有子模块,但我不熟悉它们,不想给出误导性信息。它们对于逻辑上分离大型模块确实很有用。

关于variables - Fortran 中的主机关联与使用关联,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46202348/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com