はじめに:サブルーチンとは?
プログラムを書いていると、「この一連の処理、さっきも書いたな…」「ここ、もう少しスッキリさせたいな」と感じることがあります。そんな時に役立つのがサブルーチン (Subroutine) です!
サブルーチンは、特定のまとまった処理に名前をつけて、プログラムの他の場所から呼び出して使うことができる機能です。同じ処理を何度も書く必要がなくなり、プログラム全体が読みやすく、修正しやすくなります。まさに、プログラムの部品化ですね!
このセクションでは、サブルーチンの基本的な作り方 (SUBROUTINE
) と、その呼び出し方 (CALL
文) について学んでいきましょう。
SUBROUTINEの基本的な書き方
サブルーチンは SUBROUTINE
キーワードで始まり、END SUBROUTINE
で終わります。基本的な構造は以下のようになります。
SUBROUTINE subroutine_name ( argument1, argument2, ... ) IMPLICIT NONE ! 型宣言を強制するおまじない ! --- 引数の型宣言 --- [型名], INTENT([属性]) :: argument1 [型名], INTENT([属性]) :: argument2 ... ! --- サブルーチン内で使う変数の宣言 --- [型名] :: local_variable1 ... ! --- 実行したい処理 --- 処理1 処理2 ...
END SUBROUTINE subroutine_name
重要なポイントは以下の通りです。
subroutine_name
: サブルーチンの名前です。分かりやすい名前をつけましょう。( argument1, argument2, ... )
: 仮引数(Dummy Argument) と呼ばれるもので、サブルーチンが外部から受け取る値や、外部に返す値の窓口となります。引数がない場合は()
も省略できます。IMPLICIT NONE
: 変数を使う前に必ず型宣言をすることを強制します。バグを防ぐために必ず書くようにしましょう!- 引数の型宣言: 仮引数がどのような種類のデータ(整数、実数など)なのか、そしてサブルーチン内でどのように扱われるか(入力専用、出力専用、入出力両用)を
INTENT
属性 で指定します。(INTENT
については後ほど詳しく説明します。) - ローカル変数の宣言: サブルーチンの中だけで使う変数を宣言します。
- 実行したい処理: このサブルーチンが行う具体的な計算や操作を記述します。
END SUBROUTINE subroutine_name
: サブルーチンの終わりを示します。名前を省略することも可能ですが、対応が分かりやすいように書いておくことを推奨します。
簡単な例:挨拶を表示するサブルーチン
名前を受け取って、挨拶を表示する簡単なサブルーチンを見てみましょう。
SUBROUTINE print_greeting( name ) IMPLICIT NONE CHARACTER(LEN=*), INTENT(IN) :: name ! 文字列型、入力専用 WRITE(*,*) 'こんにちは、', TRIM(name), 'さん!' ! TRIM関数で前後の空白を除去
END SUBROUTINE print_greeting
このサブルーチンは name
という名前の文字列を受け取り、画面に挨拶メッセージを出力します。CHARACTER(LEN=*)
は任意の長さの文字列を受け取れることを意味し、INTENT(IN)
は受け取った name
の値をサブルーチン内で変更しない(入力専用である)ことを示しています。
CALL文によるサブルーチンの呼び出し
作成したサブルーチンを使うには、プログラムの本体や他のサブルーチン、関数から CALL
文 を使って呼び出します。
CALL subroutine_name ( actual_argument1, actual_argument2, ... )
CALL
の後に呼び出したいサブルーチン名を書き、()
の中に 実引数(Actual Argument) を指定します。実引数は、サブルーチン定義の仮引数に対応する具体的な値や変数です。
例:先の挨拶サブルーチンを呼び出す
先ほど作成した print_greeting
サブルーチンを呼び出すメインプログラムは次のようになります。
PROGRAM main_program IMPLICIT NONE CHARACTER(LEN=20) :: user_name user_name = 'Fortran学習者' ! サブルーチン print_greeting を呼び出す CALL print_greeting( user_name ) user_name = '田中 太郎' CALL print_greeting( user_name )
CONTAINS ! メインプログラム内にサブルーチンや関数を定義する場合 ! --- ここにサブルーチンの定義を置く --- SUBROUTINE print_greeting( name ) IMPLICIT NONE CHARACTER(LEN=*), INTENT(IN) :: name WRITE(*,*) 'こんにちは、', TRIM(name), 'さん!' END SUBROUTINE print_greeting
END PROGRAM main_program
このプログラムを実行すると、以下の出力が得られます。
こんにちは、Fortran学習者さん! こんにちは、田中 太郎さん!
このように、CALL
文を使うことで、print_greeting
という処理を必要な場所で簡単に再利用できます。
Tips: サブルーチンは、メインプログラムの CONTAINS
文の後、または モジュール 内に定義するのが一般的です。(モジュールについては後のステップで学びます。)
引数の受け渡しと INTENT 属性
サブルーチンで引数を扱う際に非常に重要なのが INTENT
属性 です。これは、その引数がサブルーチンにとってどのような役割を持つかを明確に示すものです。
Fortran では、デフォルトで引数は 参照渡し(Pass by Reference) のように動作することが多いです。これは、サブルーチン内で仮引数の値を変更すると、呼び出し元の実引数(変数)の値も変更されてしまう可能性があることを意味します。意図しない値の変更はバグの原因となるため、INTENT
属性を使って引数の役割を明示することが強く推奨されます。
INTENT
属性には主に以下の3種類があります。
INTENT属性 | 意味 | 説明 | 注意点 |
---|---|---|---|
INTENT(IN) | 入力 (Input) | サブルーチンはこの引数の値を参照するだけで、変更することはできません。 | 値を変更しようとするとコンパイルエラーになります。安全! |
INTENT(OUT) | 出力 (Output) | サブルーチンはこの引数に値を設定(代入)して、結果を呼び出し元に返します。サブルーチンに入った時点での値は未定義(不定)となります。 | サブルーチン内で必ず値を代入する必要があります。呼び出し前の値は期待できません。 |
INTENT(INOUT) | 入出力 (Input/Output) | サブルーチンはこの引数の値を参照することも、変更することもできます。変更した値は呼び出し元に反映されます。 | 値が変更されることを意識して使う必要があります。 |
(指定なし) | (デフォルト) | INTENT(INOUT) と同様の振る舞いをすることが多いですが、コンパイラや状況によって異なる可能性もあります。 | 非推奨! 意図しない動作を防ぐため、必ず INTENT を指定しましょう。 |
INTENT 属性を使った例:値の交換
2つの整数値を交換するサブルーチンを考えてみましょう。この場合、両方の引数を読み取り、かつ変更する必要があるため INTENT(INOUT)
を使います。
PROGRAM swap_example IMPLICIT NONE INTEGER :: a, b a = 10 b = 20 WRITE(*,*) '交換前: a = ', a, ', b = ', b CALL swap_integers(a, b) WRITE(*,*) '交換後: a = ', a, ', b = ', b
CONTAINS SUBROUTINE swap_integers( x, y ) IMPLICIT NONE INTEGER, INTENT(INOUT) :: x, y ! 入出力両用 INTEGER :: temp ! 一時的な保管用変数 ! 値を交換する処理 temp = x x = y y = temp END SUBROUTINE swap_integers
END PROGRAM swap_example
実行結果:
交換前: a = 10 , b = 20 交換後: a = 20 , b = 10
INTENT(INOUT)
を指定したことで、サブルーチン内での x
と y
の変更が、呼び出し元の変数 a
と b
に正しく反映されていることがわかります。もしここで INTENT(IN)
を指定していたら、コンパイルエラーになります。もし INTENT(OUT)
を指定していたら、交換前の値 (10, 20) を参照できず、意図した動作になりません。
このように、INTENT
属性はプログラムの意図を明確にし、安全性を高めるために不可欠な機能です。積極的に活用しましょう!
サブルーチンを使うメリット
サブルーチンを使うことには、たくさんのメリットがあります。
- コードの再利用性向上: 同じ処理を何度も書く必要がなくなり、一度書いたサブルーチンを必要な場所で呼び出すだけで済みます。
- 可読性の向上: 複雑な処理をサブルーチンとしてまとめることで、プログラム全体の流れが分かりやすくなります。「何をしたいのか」が名前で表現されるため、読みやすくなります。
- 保守性の向上: 特定の処理に変更が必要になった場合、そのサブルーチンだけを修正すれば良いため、修正箇所が限定され、デバッグや機能追加が容易になります。
- モジュール化: 関連する処理をサブルーチンとしてまとめることで、プログラムを機能ごとに部品化(モジュール化)できます。
大規模なプログラム開発はもちろん、比較的小さなプログラムでも、サブルーチンを効果的に使うことで、より品質の高いコードを書くことができます。
まとめと次のステップ
今回は、Fortran における処理の部品化の基本である SUBROUTINE
と、それを呼び出すための CALL
文 について学びました。
特に、引数の役割を明確にし、プログラムの安全性を高める INTENT
属性 の重要性を理解できたでしょうか? INTENT(IN)
, INTENT(OUT)
, INTENT(INOUT)
を適切に使い分けることが、バグの少ない、読みやすいコードを書くための鍵となります。
サブルーチンは値を返すのではなく、引数を通して結果を返したり、何らかの操作(画面出力など)を行ったりするのに使われます。
次のステップでは、サブルーチンと似ていますが、計算結果として一つの値を返すことに特化した FUNCTION
(関数) について学んでいきます。サブルーチンと関数の違いを理解し、適切に使い分けられるようになりましょう!
参考情報
- Fortran-lang Procedures: Fortran の手続き(サブルーチンと関数)に関する公式ドキュメント(英語)
https://fortran-lang.org/learn/quickstart/procedures - Fortran Tutorial – Subroutines and Functions: tutorialspoint によるサブルーチンと関数のチュートリアル(英語)
https://www.tutorialspoint.com/fortran/fortran_subroutines.htm