如何有效地设计一个迭代数组并在一组子例程中分配相应条目的 Fortran 程序?

2024-01-02

我有一个 Fortran 子例程,它接收某种类型的大型未排序数组,并且需要调用其他子例程,这些子例程负责根据其中声明的值之一来解析和存储每个项目。

In my 上一篇文章 https://stackoverflow.com/questions/73857650/how-can-i-efficiently-parse-a-large-array-and-distribute-the-corresponding-entri,我分享了一个程序,它就是这样做的,但有一些设计缺陷,比如为需要解析的每种类型分配一个大数组,并且只填写所需的值,或者调用if (.not. allocated())每个数组元素多次。

我创建了该程序的另一个版本来解决这些缺点,但也带来了一些其他需要改进的设计范例问题:

module animal_farm

  integer :: &
    RABBIT_ID = 1, &
    DOG_ID= 2, &
    BIRD_ID= 3, &
    HORSE_ID= 4, &
    current_animal_id

  type :: Animal
    character(256) :: animal_type
    integer :: &
      age
  end type Animal

  type(Animal), dimension(:), allocatable, target :: & ! temporary arrays storing all the entries from large_animal_list for each animal
    rabbit_entries, &
    horse_entries, &
    bird_entries, &
    dog_entries

  type(Animal), dimension(:), pointer :: &
    current_animal_list

  integer, dimension(:), allocatable  :: animal_list_mapping

  ! this type and array is defined for every available animal, but only Rabbit is defined here to keep this example as simple as possible
  type :: Rabbit
    integer :: &
    age, &
    estimated_carrots_eaten ! parameters like this are defined differently for each animal, requiring a new *_params array for each type
  end type Rabbit
  type(Rabbit), dimension(:), allocatable :: & ! list of rabbit entries alongside parameters calculated specifically for rabbits
    rabbit_params

  integer, dimension(4) :: & ! number of available animals is 4
    animal_ids, &
    animal_counts, & ! temporary array to count the number of animals in large_animal_list
    individual_animal_indeces ! temporary array that stores the current index of one of the animal specific lists

  contains

subroutine parse_animals(large_animal_list)
  type(Animal), dimension(:), intent(in) :: large_animal_list
  integer :: i

  allocate(animal_list_mapping(size(large_animal_list)))

  animal_counts = 0
  do i = 1, size(large_animal_list)
    select case(large_animal_list(i)%animal_type)
      case('rabbit')
        current_animal_id = RABBIT_ID
      case('horse')
        current_animal_id = HORSE_ID
      case('bird')
        current_animal_id = BIRD_ID
      case('dog')
        current_animal_id = DOG_ID
    end select
    animal_counts(current_animal_id) = animal_counts(current_animal_id)+1
  end do

  allocate(rabbit_entries(animal_counts(RABBIT_ID)))
  allocate(horse_entries(animal_counts(HORSE_ID)))
  allocate(bird_entries(animal_counts(BIRD_ID)))
  allocate(dog_entries(animal_counts(DOG_ID)))

  individual_animal_indeces = 1
  do i = 1, size(large_animal_list)
    select case(large_animal_list(i)%animal_type)
      case('rabbit')
        current_animal_id = RABBIT_ID
        current_animal_list => rabbit_entries
      case('horse')
        current_animal_id = HORSE_ID
        current_animal_list => horse_entries
      case('bird')
        current_animal_id = BIRD_ID
        current_animal_list => bird_entries
      case('dog')
        current_animal_id = DOG_ID
        current_animal_list => dog_entries
    end select
    current_animal_list(individual_animal_indeces(current_animal_id))%age = large_animal_list(i)%age
    animal_list_mapping(i) = individual_animal_indeces(current_animal_id)
    individual_animal_indeces(current_animal_id) = animal_counts(current_animal_id)+1
  end do

  if (animal_counts(RABBIT_ID)>0) call parse_rabbit_information(rabbit_entries)
  ! if (animal_counts(HORSE_ID)>0) call parse_horse_information(horse_entries)
  ! if (animal_counts(BIRD_ID)>0) call parse_bird_information(bird_entries)
  ! if (animal_counts(DOG_ID)>0) call parse_dog_information(dog_entries)

end subroutine parse_animals


subroutine parse_rabbit_information(rabbit_entries)
  type(Animal), dimension(:), intent(in) :: rabbit_entries
  integer :: i

  allocate(rabbit_params(size(rabbit_entries)))
  do i=1, size(rabbit_entries)

    rabbit_params(i)%age = rabbit_entries(i)%age
    rabbit_params(i)%estimated_carrots_eaten = rabbit_entries(i)%age*10*365
  end do
end subroutine parse_rabbit_information

subroutine feed_rabbit(animal_list_index)
  integer, intent(in) :: animal_list_index
  integer :: rabbit_params_index

  rabbit_params_index = animal_list_mapping(animal_list_index)
  rabbit_params(rabbit_params_index)%estimated_carrots_eaten = rabbit_params(rabbit_params_index)%estimated_carrots_eaten+1
end subroutine feed_rabbit

end module animal_farm

Program TEST

    use animal_farm

    type(Animal), dimension(10) :: my_animal_list

    my_animal_list(1)%animal_type = "rabbit"
    my_animal_list(1)%age = 5
    my_animal_list(2)%animal_type = "dog"
    my_animal_list(2)%age = 6
    my_animal_list(3)%animal_type = "horse"
    my_animal_list(3)%age = 1
    my_animal_list(4)%animal_type = "rabbit"
    my_animal_list(4)%age = 3
    my_animal_list(5)%animal_type = "bird"
    my_animal_list(5)%age = 4
    my_animal_list(6)%animal_type = "horse"
    my_animal_list(6)%age = 6
    my_animal_list(7)%animal_type = "rabbit"
    my_animal_list(7)%age = 2
    my_animal_list(8)%animal_type = "rabbit"
    my_animal_list(8)%age = 2
    my_animal_list(9)%animal_type = "dog"
    my_animal_list(9)%age = 4
    my_animal_list(10)%animal_type = "horse"
    my_animal_list(10)%age = 7
    call parse_animals(my_animal_list)
    call feed_rabbit(1)
    call feed_rabbit(4)
End Program TEST

此版本仅调用负责处理不同项目类型的每个子例程一次,并传递一个已经具有正确大小并且可以简单地在目标子例程中分配的数组。如果可以的话,我想改进以下几点:

  1. 当前的解决方案涉及使用两个循环,第一个循环计算每个项目类型的出现次数,另一个循环使用相应的值填充现在分配给子例程的数组。这需要使用辅助数组,例如animal_counts or individual_animal_indeces,反过来还需要知道需要考虑多少种不同类型的动物(在示例中硬编码为 4)。我还尝试使用某种链表结构来改进这一点,这使我只能使用一个循环,但与每种类型相对应的值仍然需要存储在正确大小的数组中。

  2. 为了解决第一点中的问题,我考虑将定义的*_ID数组中的变量,因此可以使用以下方式定义辅助数组integer, dimension(size(animal_id_array))。定义的*_ID变量也被用作数组索引,这要求它们从 1-x 手动定义。每次添加或删除 id 时,都必须从这样的列表中添加和删除 id,并重新定义存储它们的数组,这不是很干净。 id 的生成可以通过以下方式实现enum, bind(c); enumerator运算符,但要获取 ids 的数量,您仍然需要创建一个单独的数组或在某处硬编码该数量。

如何修改该程序以提高其性能和内存效率,而又不会造成不必要的阅读和维护困难?


如何修改该程序以提高其性能和内存效率,而又不会造成不必要的阅读和维护困难?

同时实现这三个目标几乎总是困难的,有时甚至是根本不可能的。除非您有特殊原因不这样做,否则我建议您首先专注于使代码易于阅读和维护,然后才尝试提高其性能和内存效率。后一步只能在分析代码以查看哪些位实际需要优化之后才进行。

考虑到这一点,让我们看看是否可以稍微简化您的代码。既然您已经有了多种类型,那么让我们完全面向对象,并引入一些多态性。

如果我们继承Rabbit from Animal,我们可以避免存储animal_type字段,而是使用类型绑定过程生成它,例如

module animal_mod
  implicit none
  
  ! Define the base Animal type.
  type, abstract :: Animal
    integer :: age
  contains
    procedure(animal_type_Animal), deferred, nopass :: animal_type
  end type

  ! Define the interface for the `animal_type` functions.
  interface
    function animal_type_Animal() result(output)
      character(256) :: output
    end function
  end interface
end animal_mod

and

module rabbit_mod
  use animal_mod
  implicit none
  
  ! Define the `Rabbit` type as an extension of the `Animal` type.
  ! Note that `Rabbit` has an `age` because it is an `Animal`.
  type, extends(Animal) :: Rabbit
    integer :: estimated_carrots_eaten
  contains
    procedure, nopass :: animal_type => animal_type_Rabbit
  end type

contains

  ! Define the implementation of `animal_type` for the `Rabbit` type.
  function animal_type_Rabbit() result(output)
    character(256) :: output
    output = "rabbit"
  end function
end module

现在我们希望能够创建一系列动物。 Fortran 不允许多态数组,因此我们需要定义一个包含动物并且可以制成数组的类型。就像是

module animal_box_mod
  use animal_mod
  implicit none
  
  type :: AnimalBox
    class(Animal), allocatable :: a
  end type
end module

我们现在可以创建一系列动物,例如

type(AnimalBox) :: animals(3)

animals(1)%a = Rabbit(age=3, estimated_carrots_eaten=0)
animals(2)%a = Frog(age=3, estimated_bugs_eaten=4, length=1.7786)
animals(3)%a = Mouse(age=4, estimated_cheese_eaten=7, coat="Yellow")

而不是使用类似的方法feed_rabbit(7),您可以改用类型绑定方法。如果我们将其添加为

module rabbit_module
  type, extends(Animal) :: Rabbit
    ... ! as above
  contains
    ... ! as above
    procedure :: feed
  end type
contains
  ... ! as above
  subroutine feed(this)
    class(Rabbit), intent(inout) :: this
    this%estimated_carrots_eaten = this%estimated_carrots_eaten + 1
  end subroutine
end module

然后我们可以使用我们的animals数组为

select type(a => animals(1)%a); type is(Rabbit)
  a.feed()
end select
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何有效地设计一个迭代数组并在一组子例程中分配相应条目的 Fortran 程序? 的相关文章

  • 在 Java 中调整数组大小同时保留当前元素?

    我已经在Java中搜索了调整数组大小的方法 但找不到调整数组大小的方法同时保留当前元素 我发现例如这样的代码int newImage new int newWidth 但这会删除之前存储的元素 我的代码基本上会这样做 每当添加新元素时 数组
  • StartCoroutine 被调用多次 (C# Unity)

    我正在 Unity 中创建一个弹出菜单选项 现在我的问题是我在 void update 中创建的协程被调用了很多次 我的意思是在我的 Unity 控制台上 Debug Logs 正在递增 它不应该正确 因为它已经是协程了 有人可以帮助我了解
  • 下面的 C 程序的输出是什么? [复制]

    这个问题在这里已经有答案了 char getString char str Will I be printed return str int main printf s getString getchar 输出不应该是 我会被打印吗 相反
  • 如何在 Fortran 中实现数组结构而不是结构数组?

    我正在使用 Fortran 编写有关 CFD 主题的代码 在与一些计算机科学领域的朋友讨论后 他们告诉我 如果在他 她的代码中实现数组结构 SoA 而不是结构数组 AoS 可以加快计算时间 我见过很多关于这个主题的实现的例子 但大多数都是用
  • 如何创建通用原始数组?

    这是来自以下问题创建泛型数组的两种方法 https stackoverflow com questions 17204788 two methods for creating generic arrays 通过给定的两种方法 Suppres
  • 将字符串数组转换为字节数组

    好吧 在有人试图将其标记为重复之前 我是not要求将字符串转换为字节数组 我想要一个字符串数组 包含类似这样的内容 5 168 188 28 29 155 转换为字节数组 我已经搜索过 只能找到字符串到字节数组 这是完全不同的 谢谢 编辑
  • Swift3 中的数组排序

    在我的代码中 我有一个如下所示的结构 struct Object var name String var count Int 我现在正在创建一个包含 10 个对象的数组 这些对象具有随机名称和随机计数 有没有一个简单的方法a 按字母顺序对它
  • 如何在 Perl 中生成数组的所有排列?

    生成所有内容的最佳 优雅 简单 高效 方式是什么 n perl 中数组的排列 例如 如果我有一个数组 arr 0 1 2 我想输出所有排列 0 1 2 0 2 1 1 0 2 1 2 0 2 0 1 2 1 0 它可能应该是一个返回迭代器的
  • JavaScript 中多个数组的笛卡尔积

    如何在 JavaScript 中实现多个数组的笛卡尔积 举个例子 cartesian 1 2 10 20 100 200 300 应该返回 1 10 100 1 10 200 1 10 300 2 10 100 2 10 200 2020
  • 将堆分配的指针转换为指向 VLA 的指针是否安全?

    如果我有一个指向代表典型的堆分配空间的指针 行主二维数组 将此指针强制转换为 指向 VLA 的等效指针以方便下标 例子 Assuming m was allocated and initialized something like int
  • JavaScript 预分配数组未捕获 RangeError:数组长度无效

    我有一个小循环的代码 它抛出 Uncaught RangeError Invalid Array Length 我能够在 Google Chrome 控制台中重现它 const COUNT 100 000 000 const xValues
  • 在 ASP.NET Core 中全局重用变量

    我必须强制这些变量在我想使用的每个变量上重用 这让我很困难 我需要创建一个类来定义这些变量并在整个程序中使用它们 我怎样才能做到这一点 string RootFolderName Uplaod string ProductPictureFo
  • 如何强制 Unity 创建一个新实例?

    使用 Unity 应用程序块 当我们调用时 如何强制 Unity 配置创建对象的新实例UnityContainer Resolve
  • 错误:(1) 处的分配中的等级 0 和 1 不兼容

    我正在不规则网格上使用有限差分方法 这是代码的重要部分 IMPLICIT DOUBLE PRECISION A Z REAL 16 IPSI ICORR POT 20000 VA 20000 delta1 20000 delta2 2000
  • C++ 指针数组

    Code include stdafx h include
  • 用于存储和检索每个用户敏感数据的.Net 设计模式

    Net 服务器应用程序是否有与存储和检索敏感的每个用户信息 例如第 3 方凭据 相关的参考模式 我的初步设计思路是 生成具有适当强私钥的自签名 X509 证书 导出证书和密钥并将其存储在 USB 密钥中 该 USB 密钥将被锁在宝箱中并由龙
  • Matlab中反转一位逻辑位

    是否存在更好的方法来反转 X 的元素 gt gt X dec2bin 10 X 1010 我这样做了 x i num2str 1 str2num x i 如果我理解正确的话 你想将一位设置为 1 使用bitset bitset x bitN
  • 为什么我要使用责任链而不是 switch 语句

    考虑一下您已经获得了多次验证 仅当要检查的对象属于某种类型时 这些验证才应生效 为什么我要使用责任链而不是 switch 语句 责任链示例 public class Executor Inject private ValidatorFact
  • 接口不匹配 - 高阶函数

    我正在尝试在 Fortran 中 重现 高阶函数 module rk4 contains pure function f t x result fx real dimension 1 intent in x real intent in t
  • 使用 php 和 symfony 从数组创建 Excel 文件

    我正在尝试使用 PHP 和 symfony 将数组导出为 XLS 文件 如下面的代码所示 创建 XLS 文件后 我只能获取数组的最后一行 并且它显示在文件的第一行中 似乎 lignes 变量没有增加 我不明白出了什么问题 有人可以帮忙吗 f

随机推荐