派生类型中的可变长度数组

Posted

技术标签:

【中文标题】派生类型中的可变长度数组【英文标题】:variable length array in derived type 【发布时间】:2019-03-14 14:29:20 【问题描述】:

我主要使用 Python 进行科学编程,没有大量的 Fortran (90/95) 经验。对于我的一个项目,我想定义一个派生类型并为该类型重载一堆运算符。至关重要的是,我希望派生类型的变量之一是可变长度的数组;至少,我在代码的不同部分需要两种不同的长度。我怎样才能最有效地实现这一目标并避免代码重复?

我的第一种方法是使用可分配数组,但这涉及整个代码中的多个分配语句,包括重载的运算符。这也导致在 MPI 应用程序中使用代码时遇到困难。

我目前的方法是在两个单独的模块中定义一个同名的类型,并在代码的不同部分使用一个或另一个。可以使用头文件(下例中的mytype_operators.h)共享重载的运算符。

    module mod1
      type mytype
        real :: x1
        real,dimension(1) :: x2
      end type mytype
#include "mytype_operators.h"
    end module mod1

    module mod2
      type mytype
        real :: x1
        real,dimension(10) :: x2
      end type mytype
#include "mytype_operators.h"
    end module mod2

不幸的是,代码中有一个模块带有我想用于两种类型的子例程。目前我有该代码的两份副本;一个带有“use mod1”,另一个带有“use mod2”。我可以改进这一点并避免代码重复吗?

【问题讨论】:

你知道参数化派生类型吗? 我不是……现在正在寻找它。 This recent question 有这样的应用程序的答案。还有其他的。这些问题没有特别解决的是与 MPI 的交互。 【参考方案1】:

您的案例非常适合使用名为 parameterized derived types 的 2003 年标准中引入的 Fortran 功能(后来被编译器采用)。首先,您应该检查编译器的合规性状态,以了解它是否完全受支持。

此功能允许您在声明或构造派生类型时传递自定义参数,因此内部功能将相应调整。它们有利于将不同的行为选择组合在一个类型名称中,可能具有相当大的编码或存储差异。有两种类型的参数:

kind 参数的行为很像内在类型的种类说明符。所有变量的种类参数必须在编译时已知,并且实际上被视为常量值。方便的是您可以通过仅更改声明或构造中的值来轻松更改它(在代码时间)。这通常用于特化内在类型的组件类型。 len 参数的行为很像固有字符类型的 len 说明符。您可以在编译时或运行时定义 len 参数,并且变量的 len 参数不能更改,除非您将其声明为可分配的。此外,您可以在过程中使用带有假定 len 参数的参数,并避免代码冗长。这通常用作派生类型的“长度”或“维度”参数,因为您可以在声明数组边界和字符长度时使用它们。

一般来说,类型参数用于模拟派生类型中固有类型的功能,但您也可以“创造性”并将其用于其他事情,例如转换矩阵类型的维度空间;对于自定义“联合类型”(如枚举);作为“测量单位”数据类型中物理量的性质(用“质量”单位注释的实际值与“温度”单位不直接兼容,但它们可以以几乎相同的方式处理代码);元组类型的“arity”...

module mod1
  type :: mytype(n)
    integer, len :: n
    real :: x1
    real, dimension(n) :: x2
  end type mytype
contains
  ! your operations here...
end module mod1

并像这样使用它:

program test_pdt
  use mod1
  implicit none

  type(mytype(10)) :: mt10
  type(mytype(1)) :: mt1
  integer :: i

  mt10%x1 = 40.0
  mt10%x2 = [(0.5 * i, i = 1, 10)]
  mt1 = mytype(1)(20.0, [30.0])

  call sub(mt10)
  call sub1(mt1)

contains
  subroutine sub(m)
    ! accepts values with any "n" parameter
    type(mytype(*)) :: m
    ! you can also use them in declarations
    integer, dimension(m%n + 1) :: y
    type(mytype(m%n)) :: w
    print *, m%n, w%n, size(y)
  end

  subroutine sub1(m)
    type(mytype(1)) :: m  ! only accepts values with n=1
    print *, m%x1, m%x2, m%n
  end
end

警告: 这是一项功能,尽管多年前就已宣布,但最近才添加到大多数编译器中,您应该知道在实现中仍然存在一些错误。你应该可以正常使用,但我经常在某些极端情况下遇到错误的语法错误,有时甚至是 ICE。

【讨论】:

我刚刚尝试了这个解决方案,不幸的是,我一直使用的编译器似乎不喜欢它。稍后我会尝试使用不同的。因此,虽然此解决方案需要 Fortran 2003 或更高版本,但它完全符合我的要求。太好了,您还提供了一个使用 type(mytype(*)) 允许不同参数化类型作为输入的示例。 如果你的编译器是 gfortran 8,问题可能是mt1 = mytype(1)(20.0, [30.0])。已经报告了这个错误,但直到我检查 gfortran 9 的最后一个开发版本,它仍然存在。作为一种解决方法,此编译器接受稍有不同(且不一致)的语法:mt1 = mytype(1, 20.0, [30.0])

以上是关于派生类型中的可变长度数组的主要内容,如果未能解决你的问题,请参考以下文章

C++ 类型将基础对象转换为派生对象

java集合框架和泛型

java 集合框架

在类定义中使用内置数组,但大小推迟到派生类会导致隐藏吗?

为啥派生类对象的指针数组无法声明

c++派生类的类型列表