はじめに:サブルーチンとは? 🤔
プログラムを書いていると、「この一連の処理、さっきも書いたな…」「ここ、もう少しスッキリさせたいな」と感じることがあります。そんな時に役立つのがサブルーチン (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
コメント