1. 導入:なぜグローバル状態の管理が重要なのか
数値計算の現場において、プログラムが複雑化すると「どの変数がいつ、どこで書き換わったのか」を追跡することが困難になります。特にモジュールレベルの変数を無防備に公開すると、複数の手続きから意図しない依存関係が生じ、計算結果の再現性を損なう「副作用」を引き起こします。本稿では、モジュールを用いたカプセル化により、状態の変更を制御し、計算の決定論的性質を維持する設計手法を解説します。
2. 基礎知識:カプセル化と副作用の制御
「副作用(Side Effect)」とは、関数が引数以外の外部変数を書き換えてしまう現象を指します。計算プログラムにおいてこれが多発すると、テストやデバッグが困難になります。
これを解決する手段が「カプセル化」です。変数をモジュールの `PRIVATE` 属性で隠蔽し、値の操作を専用の「ゲッター(取得関数)」や「セッター(変更手続き)」経由に限定することで、データの不正な書き換えを防ぎ、安全な状態管理が可能になります。
3. 実装・解決策:インターフェースの制限
実装の肝は、変数の露出を最小限にすることです。モジュール内で変数を `PRIVATE` 宣言し、公開したい手続きだけを `PUBLIC` としてリストアップします。これにより、外部からは内部データに直接アクセスできず、必ず定義された手続きを通す必要が生じるため、計算の論理フローが強制的に整理されます。
4. サンプルプログラム
以下は、物理定数などの状態を安全に管理するFortranのモジュール実装例です。
MODULE state_manager
IMPLICIT NONE
! PRIVATEをデフォルトにすることで、意図しないアクセスを防ぐ
PRIVATE
! 外部から直接触らせない内部変数
REAL(8), SAVE :: tolerance = 1.0e-6_8
! 公開するインターフェース(ゲッターとセッター)
PUBLIC :: get_tolerance, set_tolerance
CONTAINS
! 状態を取得するPURE手続き(副作用がないことを保証)
PURE FUNCTION get_tolerance() RESULT(val)
REAL(8) :: val
val = tolerance
END FUNCTION get_tolerance
! 状態を更新する手続き(入力値のバリデーションが可能)
SUBROUTINE set_tolerance(new_val)
REAL(8), INTENT(IN) :: new_val
! 負の値が設定されないようガード節を挿入
IF (new_val > 0.0_8) THEN
tolerance = new_val
ELSE
PRINT , “Error: Tolerance must be positive.”
END IF
END SUBROUTINE set_tolerance
END MODULE state_manager
5. 応用・注意点:現場で役立つアドバイス
この手法を用いる際の最大のメリットは「デバッグのしやすさ」です。セッター(set_tolerance)にブレークポイントを置くことで、値が変更されるタイミングを確実に特定できます。
注意点:
・パフォーマンスの考慮: ループ内で毎回ゲッターを呼び出すとオーバーヘッドになる場合があります。その場合は、ループの直前で一度ローカル変数に読み出してから計算を回すようにしてください。
・マルチスレッド環境: Fortranのモジュール変数はデフォルトで共有されます。OpenMPなどを用いる際は、`THREADPRIVATE` 属性の併用や、そもそも状態を持たない「イミュータブル(不変)」な設計を検討することが、並列計算におけるバグ回避の定石です。

コメント