fortran66のブログ

fortran について書きます。

派生型の再帰的割り付け成分

Fortran 2008 では、派生型の成分に自分自身を allocatable 属性で持てます。つまり再帰的に派生型を定義できます。再帰的定義は古典的なリスト構造によく使われます。

Fortran 2003 までは、派生型の再帰的な成分は pointer 型に限られていました。pointer 型の成分に記憶領域の割り付けをする場合、解放時の処理を利用者側が自分でやらなければなりませんでした。記憶領域の解放漏れが起きないよう慎重に解放処理を用意する必要がありました。

ところが allocatable 属性の場合は、記憶領域解放時の処理を、処理系側が引き受けてくれるので、利用者側の負担が大幅に減ります。

根元を解放すれば、そこから連なる子・孫・末代まで皆、処理系側が解放してくれます。根元を解放するとそこから連なる記憶領域がアクセス不能の死に領域になってしまう pointer 型との大きな違いです。

ソースプログラム

古典的な一次元単方向リストを定義して、整数値を入れてゆきます。つぎに根元からリストをたぐって代入した整数値を出力します。最後に、リストの根元を解放します。この時、ファイナライザ(いわゆるデストラクタ)を用意して、いまわの際に成分を出力させています。

    module m_final
      type :: t_list
        integer :: ival
        type (t_list), allocatable :: next
      contains
        final :: destruct
      end type t_list  
    contains
      subroutine destruct(this)
        type (t_list), intent(in) :: this
        print *, this%ival
      end subroutine destruct
    end module m_final
    
    program final
      use m_final
      implicit none
      type (t_list), allocatable, target :: root
      type (t_list), pointer :: last
      integer :: i
      allocate(root, source = t_list(0))
      last => root
      do i = 1, 10
        allocate(last%next, source = t_list(i))
        last => last%next
      end do    
      last => root
      
      print *, 'print list'
      do 
        if (.not. associated(last)) exit
        print *, last%ival
        last => last%next
      end do  
      
      print *, 'finalizer'
      deallocate(root)
    end program final

実行結果

ファイナライズする順番は枝葉の末端から根元に向かってではなく、根元から枝葉の末端に向かって解放されてゆくようです。

 print list
           0
           1
           2
           3
           4
           5
           6
           7
           8
           9
          10
 finalizer
           0
           1
           2
           3
           4
           5
           6
           7
           8
           9
          10
続行するには何かキーを押してください . . .

参考
・M.Metcalf, J.Reid and M.Cohen: Modern Fortran Explained 20.3.1
・J.Reid: The new features of Fortran 2008