「allocatable属性の配列を成分として持った構造体」の配列を取り扱う際に生じた問題と対処について記録として残しておく。
問題
異なる長さの配列を更に配列でまとめて扱う際には構造体で丸め込むのが簡単な対処法となる。しかし、2タイプのリストlist_b(1,:),list_b(2,:)、(実際にはlist_b1(:),list_b2(:)という異なる配列で書いていた。)で集めた配列の結果を一つの配列list_a(1:2,:)にまとめるという代入処理を行う際に問題が生じた。
下の失敗した出力結果のように部分的に代入を行った場合、意図した部分に代入が行われていない。ループを用いて個々の配列成分に代入を行ったり、サンプルのように2タイプの配列を多次元配列でまとめておき一度に代入するとうまくいくらしい。
原因についてはよくわからないが考えてみた。allocatable属性の配列を成分として持った構造体を代入する際にはallocatable配列の再割付が行われると思われるが、配列にスライスを使って代入を行った場合そこらへんがうまく行っていないようだ。これはintel fortran 18.0.3及びgcc5.4.0のgfortranで確認した。
対処
とりあえずループを使って代入すれば回避可能。また、新しいintelのコンパイラのベータ版(intel fortran 19.0.0.070 Beta)で試したところうまくいったので(期待する出力のほう)今後修正されていくのかもしれない。
program test
  implicit none
  
  ! allocatable属性の配列を持つ構造体
  type testType
    integer, allocatable :: vector(:)
  end type testType
  
  integer, parameter :: data_type = 2
  integer, parameter :: data_len = 3
  integer :: i, j
  type(testType) :: list_a(data_type,data_len) ! 代入される側
  type(testType) :: list_b(data_type,data_len) ! 代入する側
  ! データ初期化
  do i = 1, data_len
    do j = 1, data_type
      list_b(j,i)%vector = [(j, j=1,i)]
    end do
  end do
  ! これが失敗
  print *, "--- 配列をタイプごとに代入 ---"
  do i = 1, data_type
    list_a(i,:) = list_b(i,:)
  end do
  ! 出力
  do i = 1, data_len
    do j = 1, data_type
      if (allocated(list_a(j,i)%vector)) then
        print *, i, j, " th item : ", list_a(j,i)%vector(:)
      else
        print *, i, j, " th item : ", "not allocated"
      end if
    end do
  end do
  
  ! これはうまくいく
  print *, "--- 全部まとめて代入 ---"
  list_a(:,:) = list_b(:,:)
  ! 出力
  do i = 1, data_len
    do j = 1, data_type
      if (allocated(list_a(j,i)%vector)) then
        print *, i, j, " th item : ", list_a(j,i)%vector(:)
      else
        print *, i, j, " th item : ", "not allocated"
      end if
    end do
  end do
  ! これはうまくいく
  print *, "--- 一つずつ代入 ---"
  do i = 1, data_len
    do j = 1, data_type
      list_a(j,i) = list_b(j,i)
    end do
  end do
  ! 出力
  do i = 1, data_len
    do j = 1, data_type
      if (allocated(list_a(j,i)%vector)) then
        print *, i, j, " th item : ", list_a(j,i)%vector(:)
      else
        print *, i, j, " th item : ", "not allocated"
      end if
    end do
  end do
end program test
 --- 配列を半分ずつ代入 ---
           1           1  th item :            1
           1           2  th item :            1           2
           2           1  th item :            1           2           3
           2           2  th item : not allocated
           3           1  th item : not allocated
           3           2  th item : not allocated
 --- 全部まとめて代入 ---
           1           1  th item :            1
           1           2  th item :            1
           2           1  th item :            1           2
           2           2  th item :            1           2
           3           1  th item :            1           2           3
           3           2  th item :            1           2           3
 --- 一つずつ代入 ---
           1           1  th item :            1
           1           2  th item :            1
           2           1  th item :            1           2
           2           2  th item :            1           2
           3           1  th item :            1           2           3
           3           2  th item :            1           2           3
 --- 配列を半分ずつ代入 ---
           1           1  th item :            1
           1           2  th item :            1
           2           1  th item :            1           2
           2           2  th item :            1           2
           3           1  th item :            1           2           3
           3           2  th item :            1           2           3
 --- 全部まとめて代入 ---
           1           1  th item :            1
           1           2  th item :            1
           2           1  th item :            1           2
           2           2  th item :            1           2
           3           1  th item :            1           2           3
           3           2  th item :            1           2           3
 --- 一つずつ代入 ---
           1           1  th item :            1
           1           2  th item :            1
           2           1  th item :            1           2
           2           2  th item :            1           2
           3           1  th item :            1           2           3
           3           2  th item :            1           2           3