配列#

配列はFortranの中心的なオブジェクトです。動的サイズ配列の作成については、割付け可能配列のセクションで説明します。

配列をプロシージャに渡すには、4つの方法があります。

  1. 想定形状配列

  2. 想定ランク配列

  3. 明示形状配列

  4. 想定サイズ配列

プロシージャに配列を渡すための推奨される方法は、想定形状配列として渡すことです。

subroutine f(r)
  real(dp), intent(out) :: r(:)
  integer :: n, i
  n = size(r)
  do i = 1, n
    r(i) = 1.0_dp / i**2
  end do
end subroutine f

高次元配列も同様の方法で渡すことができます。

subroutine g(A)
  real(dp), intent(in) :: A(:, :)
  ...
end subroutine g

配列は単純に以下のように渡されます。

real(dp) :: r(5)
call f(r)

この場合、配列のコピーは行われません。これには、形状とサイズ情報が自動的に渡され、コンパイル時、そしてオプションで実行時にチェックされるという利点があります。同様に、配列のストライドは、配列のコピーを必要とせずに、想定形状記述子として渡すことができます。

real(dp) :: r(10)
call f(r(1:10:2))
call f(r(2:10:2))

これは、サブルーチンに配列を渡す際のデフォルトの方法とするべきです。配列全体をスライスとして渡すことは、コードの実際の意図をわかりにくくするため、避けてください。

real(dp) :: r(10)
call f(r(:))

より一般的な配列をプロシージャに渡す必要がある場合は、Fortran 2018規格で導入された想定ランク機能を使用できます。

subroutine h(r)
  real(dp), intent(in) :: r(..)
  select rank(r)
  rank(1)
  ! ...
  rank(2)
  ! ...
  end select
end subroutine h

実際のランクは、実行時にselect rank構文を使用してクエリできます。これにより、異なる配列ランクを処理する必要がある、より汎用的な関数を簡単に作成できます。

明示形状配列は、関数からデータを返す場合に役立ちます。その機能のほとんどは、想定形状配列と想定ランク配列によって提供できますが、CとのインターフェースやレガシーFortranプロシージャで頻繁に使用されるため、ここで簡単に説明します。

明示形状配列を使用するには、以下の例のように、次元を仮引数として明示的に渡す必要があります。

subroutine f(n, r)
  integer, intent(in) :: n
  real(dp), intent(out) :: r(n)
  integer :: i
  do i = 1, n
    r(i) = 1.0_dp / i**2
  end do
end subroutine

高次元配列の場合、追加のインデックスを渡す必要があります。

subroutine g(m, n, A)
  integer, intent(in) :: m, n
  real(dp), intent(in) :: A(m, n)
  ...
end subroutine

ルーチンは以下のように呼び出すことができます。

real(dp) :: r(5), s(3, 4)
call f(size(r), r)
call g(size(s, 1), size(s, 2), s)

形状はチェックされないため、以下は、潜在的に正しくない結果をもたらす可能性のある、有効なコードとなります。

real(dp) :: s(3, 4)
call g(size(s), 1, s)  ! s(12, 1) in g
call g(size(s, 2), size(s, 1), s)  ! s(4, 3) in g

この場合、メモリレイアウトは保持されますが、形状は変更されます。また、明示形状配列は連続したメモリを必要とし、非連続の配列ストライドが渡された場合は一時配列を作成します。

明示形状を使用して関数から配列を返すには、以下を使用します。

function f(n) result(r)
  integer, intent(in) :: n
  real(dp) :: r(n)
  integer :: i
  do i = 1, n
    r(i) = 1.0_dp / i**2
  end do
end function

最後に、想定サイズ配列があります。これは、コンパイル時と実行時のチェックが最も少なく、レガシーコードで頻繁に見られます。想定形状配列または想定ランク配列を優先して使用してください。想定サイズ配列の仮引数は、最後の次元としてアスタリスクで識別されます。これは、sizeshapeなどの多くの組込み関数でこの配列を使用することを無効にします。

想定形状配列の正しいサイズと形状を確認するには、sizeおよびshape組込み関数を使用して、これらのプロパティをクエリできます。

if (size(r) /= 4) error stop "Incorrect size of 'r'"
if (any(shape(r) /= [2, 2])) error stop "Incorrect shape of 'r'"

sizeはすべての次元の合計サイズを返します。特定の次元の形状を取得するには、それを関数の2番目の引数として追加します。

配列は、配列コンストラクタを使用して初期化できます。

integer :: r(5)
r = [1, 2, 3, 4, 5]

配列コンストラクタには、構築される配列の型を注釈として付けることができます。

real(dp) :: r(5)
r = [real(dp) :: 1, 2, 3, 4, 5]

暗黙のdoループは、配列コンストラクタ内でも使用できます。

integer :: i
real(dp) :: r(5)
r = [(real(i**2, dp), i = 1, size(r))]

配列を1以外のインデックスから開始するには、以下を実行します。

subroutine print_eigenvalues(kappa_min, lam)
  integer, intent(in) :: kappa_min
  real(dp), intent(in) :: lam(kappa_min:)

  integer :: kappa
  do kappa = kappa_min, ubound(lam, 1)
    print *, kappa, lam(kappa)
  end do
end subroutine print_eigenvalues