Fortran 95 及更高版本中过时的 "statement functions" 的替代品?

Replacement for obsolete "statement functions" in Fortran 95 and later?

原问题

据我了解,从 Fortran 95 开始,声明函数已被宣布为过时,取而代之的是内部函数。但是,内部函数并不涵盖所有用例,特别是当使用语句函数作为带有隐含参数的缩写以使长公式的实现更具可读性时。这个用例有足够的替代品吗?

例子

例如,假设我们有一个关系

Cᵢⱼ = ∫dx₁∫dx₂∫dx₃∫dy₁∫dy₂∫dy₃ (AᵢⱼBᵢⱼ +BᵢⱼAⱼᵢ)

例如将直接使用 A、B 的内部表示的实现与先前代码规定的索引顺序进行比较,结果

do a1=1,NGRID1; do a2=1,NGRID2; ...; do aN=1,NGRIDN
  x(i,j) = x(i,j) &
      & + ARR_A(x1,x2,x3,i,y1,y2,y3,j)*ARR_B(x1,x2,x3,i,y1,y2,y3,j) &
      & + ARR_B(x1,x2,x3,i,y1,y2,y3,j)*ARR_A(x1,x2,x3,i,y1,y2,y3,j)
end do; end do; ...; end do

使用语句函数作为缩写的实现,

A(i,j) = ARR_A(x1,x2,x3,i,y1,y2,y3,j)
B(i,j) = ARR_B(x1,x2,x3,i,y1,y2,y3,j)
...
do x1=1,NGRID1; do x2=1,NGRID2; ...; do y3=1,NGRIDN
  x(i,j) = x(i,j) + A(i,j)*B(i,j) + B(i,j)*A(i,j)
end do; end do; ...; end do

在第二个版本中,在注意到 问题后,似乎更容易发现索引的混合顺序。

使用宏?

我发现实现类似语义的唯一方法是使用预处理器宏。通常,使用 C 风格的宏,这将导致

#define A(i,j) ARR_A(x1,x2,x3,i,y1,y2,y3,j)

但这会牺牲名称的范围。虽然这看起来可能很有用,尤其是在多个函数中使用相同的缩写时,但它似乎可能相当混乱。首先,当错误地使用语句函数时,编译器会给出比宏更有用的错误消息。

它还引入了手动 #undefine 名称的需要,如果我们想在不同的上下文中为不同的事物重复使用相同的名称。

解决方案(1)

我误解了 CONTAINS 和内部函数的工作原理(请参阅 及其注释)。

简短:内部函数可以在 CONTAINS 的子例程中定义,并且与语句函数完全一样,除了更冗长的语法。

产生误解的部分原因是不允许以这种方式在子例程中定义子例程。显然它确实有效.不知道我做错了什么。

对于这个用例,内部过程几乎是语句函数的直接替代品,它们通常具有更多功能和不易出错的优点,但代价是它们的定义略微冗长。

MODULE some_module     ! Just for the sake of example.
  ...
CONTAINS
  ! The host of the internal procedure.  This could 
  ! also be an external procedure or a main program.
  ! It could also be a function.
  SUBROUTINE some_subroutine
    ...
    DO i = 1, 3
      DO j = 1, 3
        DO x1 = 1, 3
          DO x2 = 1, 3
            ...
            x(i,j) = x(i,j) + A(i,j)*B(i,j) + B(i,j)*A(i,j)
            ...
          END DO
        END DO
      END DO
    END DO     
    ...      
  CONTAINS
    ! An internal procedure.  For different use cases 
    ! this could also be a subroutine.
    FUNCTION A(i,j)
      INTEGER, INTENT(IN) :: i,j
      REAL :: A
      A = ARR_A(x1,x2,x3,i,y1,y2,y3,j)
    END FUNCTION A

    FUNCTION B(i,j)
      INTEGER, INTENT(IN) :: i,j
      REAL :: B
      B = ARR_B(x1,x2,x3,i,y1,y2,y3,j)
    END FUNCTION B
    ...
 END SUBROUTINE some_subroutine
 ...
END MODULE some_module

请注意,Fortran 77/90/95/2003 规则下的最大数组秩为七。