fortran 实现C++的vector 或Python的List

fortran 实现C++的vector.push_back() 或Python的List.append()

fortran90, 数组需要提前分配好大小,
但是如果事先不知道一个数组要分配多少内存,
遇到一个新元素加入到数组中,再遇到一个新元素又加入到数组中,
怎样实现呢?
我们知道C++标准库中的vector动态数组,底层是数组,
但是却可以变戏法一样把新元素添加到数组尾部,比如 vec.push_back(x), 可以把x添加到vec的尾部,
这是怎么实现的呢?

实际上可以通过分配新数组,拷贝并且释放旧数组来实现,比如,

  • 分配一个中间媒介,旧数组拷贝给媒介数组
  • 释放旧数组,重新分配一个更大的数组
  • 媒介数组拷贝给新数组
  • 新元素添加到新数组的尾部

以上操作可以实现类似C++的vector.push_back() 或Python的List.append()的功能,但是要注意:

  • 减少拷贝次数:如果每次添加新元素都涉及数组之间的拷贝,那么拷贝次数非常多,时间复杂度很高。为了降低时间复杂度,可以使用C++的vector的策略,即实际分配一个内存容量更大的空间,直到元素个数超过数组容量之后,再去分配新数组以及拷贝一番 (需要扩容时,可以让新数组容量为当前size的两倍)
  • fortran中allocatable数组没法直接代入子程序或函数中allocate,需要用module 中的contain的方式
  • 我目前想出的fortran中动态添加元素的最优雅的解决方案:在fortran中写一个vector类,类似面向对象,把添加元素等操作放入类方法中。但是fortran的自定义类Type只能包含类属性,不能包含类方法。有没有更接近类的做法呢?有!把类Type封装到Module中,Module再 contains 一下类相关的子程序或函数,这样类属性和类方法都有了!

实现上述思想的fortran代码如下所示(其中的append()类似于C++中的vector.push_back() 或者Python中的 list.append()):

  module vectorType
      !!! basic idea: a smart array that can dynamically add a number to its back
      !!! similar to vector in C++
      !!! similar to List in Python
      !!!
      !!! author: mohanxuan, https://blog.csdn.net/weixin_43414513
      !!! license: Apache 2.0
      type vector
          integer :: size  ! effective size of the array of this vector
          integer :: capacity  ! the storage memory capacity (>= size) of the array of this vector
          integer, allocatable :: array(:)
      end type vector
      
  contains  ! =========================================
      
    subroutine vecInit(vec, size_)
        !!! constructor or initialize function for Type vector
        implicit none
        type(vector) vec
        integer :: size_
        
        vec%size = size_
        vec%capacity = max(size_, 1)
        allocate(vec%array(vec%capacity))
    end subroutine vecInit
  
    
    subroutine vecDele(vec)
        !!! destructor function for Type vector
        implicit none
        type(vector) vec
        
        vec%size = 0
        vec%capacity = 0
        deallocate(vec%array)
    end subroutine vecDele
    
      
    subroutine append(vec, num)
        !!! append a number to the back of this array
        !!! similar to the function push_back() in C++ vector
        !!! similar to the function append() in Python list
        !!! author: mohanxuan, https://blog.csdn.net/weixin_43414513
        implicit none
        type(vector) vec, tmp
        integer :: num
        
        if (vec%size + 1 <= vec%capacity) then
            vec%array(vec%size + 1) = num
            vec%size = vec%size + 1
        else
            call vecInit(TMP, vec%size)
            TMP%array = vec%array
            
            call vecDele(vec)
            call vecInit(vec, TMP%size * 2)  ! twice the size of original vec
            vec%size = TMP%size
            
            vec%array(1:vec%size) = TMP%array(1:vec%size)
            vec%array(vec%size + 1) = num
            vec%size = vec%size + 1
            call vecDele(TMP)
        endif
    end subroutine append

        
    subroutine extend(v1, v2)
        !!! merge vector v2 to vector v1
        !!! similar to 'v1.extend(v2)' in Python
        implicit none
        type(vector) v1, v2, tmp
        
        if (v1%size + v2%size <= v1%capacity) then
            v1%array(v1%size + 1 : v1%size + v2%size) 
 &              = v2%array(1:v2%size)
            v1%size = v1%size + v2%size
        else
            call vecInit(TMP, v1%size)
            TMP%array(1:v1%size) = v1%array(1:v1%size)
            
            call vecDele(v1)
            call vecInit(v1, TMP%size + v2%size)
            v1%array(1:TMP%size) = TMP%array(1:TMP%size)
            v1%array(TMP%size + 1 : TMP%size + v2%size) 
 &              = v2%array(1:v2%size)
            call vecDele(TMP)
        endif
    end subroutine extend
    
        
    subroutine vecPrint(vec)
        implicit none
        type(vector) vec
        print*, vec%array(1:vec%size)
    end subroutine vecPrint
      
  end module vectorType


  program main 1
  use vectorType
  implicit none
  integer :: i
  type(vector) x
  
  call vecInit(x, 0)  ! initial x to be a 0 zise vector
  do i = 1, 100
      call append(x, i)  ! push i to x's back
  enddo
  
  print*, "array of x ="
  call vecPrint(x)
  print*, "x%size =", x%size
  print*, "x%capacity =", x%capacity 
  
  end  

你可能感兴趣的:(fortran,fortran,数组,c++,python,list)