You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现元素类型可变的Fortran数组?优化现有实现方案

Fortran可变类型数组实现的优化方案探讨

背景与现有实现

我此前通过工厂方法实现了元素类型可变的数组,简化版代码如下:

module multiarray
use iso_fortran_env, only: real64
implicit none

integer, parameter :: dp = real64

! 抽象list类型
type, abstract :: list
  contains
    procedure(in),  deferred, pass(self) :: addval ! 添加变量
    procedure(out), deferred, pass(self) :: getval ! 提取变量
end type

abstract interface
  subroutine in(self,val)
    import :: list
    implicit none
    class(list), intent(inout) :: self
    class(*), intent(in) :: val
  end subroutine  
  
  subroutine out(self,val)
    import :: list
    implicit none
    class(list), intent(in) :: self
    class(*), intent(out) :: val
  end subroutine    
end interface

! real(dp)类型扩展
type, extends(list) :: real_num
  real(dp) :: var
  contains
    procedure, pass(self) :: addval => addreal
    procedure, pass(self) :: getval => getreal    
end type

! 字符型、逻辑型等类似扩展
...

! 包装器类型
type wrapper
  class(list), ALLOCATABLE :: arg
end type

contains

! real(dp)相关子程序
subroutine addreal(self,val)
  implicit none
  class(real_num), intent(inout) :: self
  class(*), intent(in) :: val
  select type(val)
    type is (real(dp))
      self%var = val
  end select  
end subroutine

subroutine getreal(self,val)
  implicit none
  class(real_num), intent(in) :: self
  class(*), intent(out) :: val
  select type(val)
    type is (real(dp))
      val = self%var
  end select  
end subroutine

! 字符型、逻辑型等类似子程序
...

! 工厂方法
function get_list(val) result(pt)
  class(*), intent(in) :: val
  class(list), allocatable :: pt
  
  select type(val)
    type is (real(dp))
      allocate(real_num :: pt)
    type is (character(*))
      ...      
  end select   
  
end function

end module

使用时通过wrapper类型构建数组:

type(wrapper) :: A(5)
real(dp) :: x
A(1)%arg = get_list(90.d0)  ! 将A(1)设为real类型     
call A(1)%arg%addval(90.d0) ! A(1)是值为90的real类型变量
call A(1)%arg%getval(x)     ! 将A(1)的值赋给x供其他地方使用    

问题

由于抽象接口中val被定义为泛型class(*)类型,我必须在addreal这类子程序中用select type来匹配参数。想请教:

  1. 能否在抽象接口内嵌套定义接口,从而省去addreal中的select type(因为明确知道这类子程序的输入是real类型)?
  2. 有没有更简便的方式实现这种元素类型可变的数组?

解答

关于抽象接口嵌套的可能性

Fortran的抽象接口不支持嵌套定义,没法通过嵌套接口直接规避select type。但可以通过给抽象类型添加泛型绑定+特定类型专用接口的方式,实现类型匹配的简化:

给抽象类型list扩展泛型接口,同时定义不同类型的专用抽象子程序:

type, abstract :: list
  contains
    generic :: addval => addval_generic, addval_real, addval_char
    generic :: getval => getval_generic, getval_real, getval_char
    procedure(in_generic), deferred, pass(self) :: addval_generic
    procedure(out_generic), deferred, pass(self) :: getval_generic
    procedure(in_real), deferred, pass(self) :: addval_real
    procedure(out_real), deferred, pass(self) :: getval_real
    procedure(in_char), deferred, pass(self) :: addval_char
    procedure(out_char), deferred, pass(self) :: getval_char
end type

abstract interface
  ! 原泛型版本
  subroutine in_generic(self,val)
    import :: list
    implicit none
    class(list), intent(inout) :: self
    class(*), intent(in) :: val
  end subroutine  
  subroutine out_generic(self,val)
    import :: list
    implicit none
    class(list), intent(in) :: self
    class(*), intent(out) :: val
  end subroutine    
  ! real类型专用版本
  subroutine in_real(self,val)
    import :: list, dp
    implicit none
    class(list), intent(inout) :: self
    real(dp), intent(in) :: val
  end subroutine  
  subroutine out_real(self,val)
    import :: list, dp
    implicit none
    class(list), intent(in) :: self
    real(dp), intent(out) :: val
  end subroutine    
  ! 字符类型专用版本
  subroutine in_char(self,val)
    import :: list
    implicit none
    class(list), intent(inout) :: self
    character(*), intent(in) :: val
  end subroutine  
  subroutine out_char(self,val)
    import :: list
    implicit none
    class(list), intent(in) :: self
    character(*), intent(out) :: val
  end subroutine    
end interface

在real_num类型中实现专用版本时,就无需select type:

type, extends(list) :: real_num
  real(dp) :: var
  contains
    procedure, pass(self) :: addval_generic => addreal_generic
    procedure, pass(self) :: getval_generic => getreal_generic
    procedure, pass(self) :: addval_real => addreal
    procedure, pass(self) :: getval_real => getreal
    procedure, pass(self) :: addval_char => addreal_char_err
    procedure, pass(self) :: getval_char => getreal_char_err
end type

contains

subroutine addreal(self,val)
  implicit none
  class(real_num), intent(inout) :: self
  real(dp), intent(in) :: val
  self%var = val ! 直接赋值,无需类型匹配
end subroutine

subroutine getreal(self,val)
  implicit none
  class(real_num), intent(in) :: self
  real(dp), intent(out) :: val
  val = self%var ! 直接赋值
end subroutine

! 泛型版本可调用专用版本或做错误处理
subroutine addreal_generic(self,val)
  implicit none
  class(real_num), intent(inout) :: self
  class(*), intent(in) :: val
  select type(val)
    type is (real(dp))
      call self%addval(val)
    class default
      error stop "addreal_generic: 类型不匹配"
  end select
end subroutine

! 类型不匹配的错误处理
subroutine addreal_char_err(self,val)
  implicit none
  class(real_num), intent(inout) :: self
  character(*), intent(in) :: val
  error stop "addreal_char_err: 无法向real_num添加字符类型值"
end subroutine

调用时编译器会自动匹配对应类型的专用接口,无需手动处理类型判断:

call A(1)%arg%addval(90.d0) ! 直接匹配addval_real
call A(1)%arg%getval(x)     ! 直接匹配getval_real

更简便的可变类型数组实现方式

如果需求仅为存储不同类型元素并提取,可考虑以下两种简洁方案:

1. 直接使用class(*)可分配数组

无需额外抽象类型和包装器,直接用泛型可分配数组存储:

class(*), allocatable :: A(5)
real(dp) :: x

allocate(real(dp) :: A(1))
A(1) = 90.d0

select type(val => A(1))
  type is (real(dp))
    x = val
end select

缺点是每次访问元素都需要select type,但代码结构极简,适合简单场景。

2. 利用Fortran 2018参数化类型

若编译器支持Fortran 2018,可通过参数化类型实现类型安全的容器:

type :: container(T)
  type(T) :: val
end type

type(container(real(dp))) :: real_arr(5)
type(container(character(20))) :: char_arr(5)

注意这种方式只能存储同类型元素,若需要混合类型,仍需结合多态或class(*)。


内容的提问来源于stack exchange,提问作者Al007

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 20:24:32