導入
数値計算を行う際、プログラムが扱うデータのサイズが事前にわからない、あるいは実行中に動的に変化するケースは少なくありません。例えば、ある条件を満たすデータだけを抽出したり、計算結果の配列サイズが入力データによって変わる場合などです。このような状況で、固定サイズの配列を使うと、メモリの無駄が生じたり、最悪の場合メモリが足りなくなってプログラムがクラッシュしてしまう可能性があります。
ここでFortranの強力な機能である「割付可能 (ALLOCATABLE)」属性が役立ちます。特に、このALLOCATABLE属性を持つ変数を手続き(サブルーチンや関数)の引数として使うことで、プログラムの柔軟性とメモリ効率を飛躍的に向上させることができます。今回は、この「ALLOCATABLE引数」の基本から使い方までを初心者の方にも分かりやすく解説していきます。
基礎知識
まずは、「割付可能 (ALLOCATABLE)」属性とその関連機能について説明しましょう。
ALLOCATABLE属性とは?
FortranにおけるALLOCATABLE属性は、プログラムの実行中にメモリ領域を動的に確保(ALLOCATE)したり、解放(DEALLOCATE)したりできる配列に与える属性です。通常の配列はコンパイル時にサイズが決定されますが、ALLOCATABLE配列はプログラムの実行中に必要なサイズを決定し、メモリを割り当てることができます。使い終わったら解放することも可能です。
なぜ引数にALLOCATABLEが必要なのか?
通常、サブルーチン内で `ALLOCATE` した配列は、そのサブルーチンが終了すると自動的に `DEALLOCATE` されてしまいます。これでは、サブルーチンで計算した結果を動的に確保した配列に格納し、その配列を呼び出し元(メインプログラムなど)に持ち帰る、ということができません。
ここで、ALLOCATABLE属性を引数に指定することで、サブルーチンが呼び出し元から「この配列にメモリを割り当てて良いよ(そしてその結果を呼び出し元で使えるようにしてね)」という権限を受け取ります。サブルーチン内で `ALLOCATE` された配列は、そのサブルーチンが終了しても `DEALLOCATE` されず、呼び出し元で引き続き利用できるようになるのです。これは、大規模な計算で柔軟なワーク領域を扱い、メモリを効率的に利用する上で非常に重要な概念です。
ALLOCATE文とDEALLOCATE文
ALLOCATABLE配列のメモリ管理には、以下の文を使います。
- ALLOCATE(配列名(サイズ), STAT=変数): 指定したサイズのメモリを配列に割り当てます。`STAT` オプションを付けると、成功・失敗のステータスコードを `変数` に格納できます(0なら成功、それ以外はエラー)。
- DEALLOCATE(配列名, STAT=変数): 割り当てられたメモリを解放します。こちらも `STAT` オプションで成功・失敗を確認できます。
intent(out)属性
今回の例では `intent(out)` が使われています。これは、引数が「サブルーチン内で値を設定され、呼び出し元に返される」ことを意味します。つまり、サブルーチンに入る時点での引数の値は無視され、サブルーチン内で新しい値が設定されます。ALLOCATABLE引数と組み合わせることで、「サブルーチン内で新しいメモリを割り当てて、呼び出し元にその結果を返す」という意図が明確になります。
実装/解決策
具体的なシナリオを考えてみましょう。ある計算結果を格納する配列を、サブルーチン側で動的に生成し、その結果をメインプログラムで利用したい場合です。
例えば、計算によって結果のベクトルのサイズが変わるような処理をサブルーチンとして実装したいとします。このサブルーチンは、呼び出し元から何の配列も受け取らず、自身で適切なサイズの配列を `ALLOCATE` し、値を設定し、その結果を呼び出し元に返します。
この方法の大きな利点は、呼び出し元がサブルーチンがどれくらいのメモリを必要とするか事前に知る必要がないことです。サブルーチンが内部で最適なメモリ管理を行い、その結果をクリーンな形で呼び出し元に渡すことができます。これにより、プログラム全体の設計がシンプルになり、保守性も向上します。
サンプルプログラム
以下のFortranプログラムは、サブルーチン内でALLOCATABLE引数を使って動的に配列を割り当て、呼び出し元に結果を返す例です。
program main_program
implicit none
! 動的に割り当てられる実数型1次元配列を宣言します。
! この配列はサブルーチンによってメモリが割り当てられます。
real, allocatable :: my_vector(:)
integer :: i, status_alloc, status_dealloc
! —————————————————-
! サブルーチンを呼び出し、my_vectorにメモリを割り当てて値を設定してもらいます。
! ここでmy_vectorはALLOCATABLE引数として渡され、サブルーチン内でALLOCATEされます。
call create_and_fill_vector(my_vector)
! —————————————————-
! サブルーチンから戻ってきた後、my_vectorのメモリが割り当てられているか確認します。
! allocated()関数は、配列が割り当てられている場合は.TRUE.、そうでなければ.FALSE.を返します。
if (allocated(my_vector)) then
print , “— サブルーチンから返されたベクトル —”
print , “割り当てられたサイズ: “, size(my_vector)
print , “要素の値:”
do i = 1, size(my_vector)
! 各要素の値を整形して表示します。
print ‘(A, I0, A, F8.2)’, ‘my_vector(‘, i, ‘) = ‘, my_vector(i)
end do
! 使い終わったので、my_vectorのメモリを解放します。
! DEALLOCATEもSTATオプションで成功・失敗を確認するのが良い習慣です。
deallocate(my_vector, stat=status_dealloc)
if (status_dealloc == 0) then
print , “— my_vectorのメモリを正常に解放しました。 —”
else
print , “!!! my_vectorのメモリ解放に失敗しました。エラーコード: “, status_dealloc, ” !!!”
end if
else
print , “!!! my_vectorは割り当てられていませんでした。サブルーチンでエラーが発生した可能性があります。 !!!”
end if
end program main_program
! —————————————————-
! サブルーチン: 動的に配列を割り当て、値を設定する
! —————————————————-
subroutine create_and_fill_vector(v)
implicit none
! 引数 ‘v’ はALLOCATABLE属性を持ち、intent(out)であるため、
! このサブルーチン内でメモリが割り当てられ、呼び出し元に結果が返されます。
! intent(out)なので、呼び出し元で既に割り当てられていても、ここで新しいメモリが割り当てられます。
real, allocatable, intent(out) :: v(:)
integer :: vec_size, i, status_alloc
real :: random_val_seed
! 配列のサイズをランダムに決定します(例として10から20の範囲)。
! random_number()は0から1の実数を返します。
call random_number(random_val_seed)
vec_size = int(10.0 + random_val_seed 10.0) ! 10から19の整数になります
print , “— create_and_fill_vectorサブルーチン内 —”
print , “決定されたベクトルサイズ: “, vec_size
! 引数 ‘v’ のメモリを割り当てます。
! STATオプションで割り当ての成功・失敗を確認します。
allocate(v(vec_size), stat=status_alloc)
if (status_alloc == 0) then
print , “vのメモリを正常に割り当てました。”
! 割り当てられた配列に値を設定します。
do i = 1, vec_size
call random_number(v(i)) ! 各要素に0から1のランダムな値を設定
v(i) = v(i) 100.0 ! 値を少し大きくして見やすくします
end do
print , “vに値を設定しました。”
else
print , “!!! vのメモリ割り当てに失敗しました。エラーコード: “, status_alloc, ” !!!”
! 割り当て失敗時は、vは未割り当ての状態のまま呼び出し元に戻ります。
! 呼び出し元でallocated(v)でチェックできるようになります。
end if
end subroutine create_and_fill_vector
応用・注意点
1. ALLOCATE失敗時のエラー処理
上記のサンプルコードでも `STAT` オプションを使っていますが、メモリの割り当ては常に成功するとは限りません。特に大規模な計算では、メモリ不足で `ALLOC

コメント