[Fortranのはじめ方] Part15: モジュール(MODULE)の活用

Fortran

プログラムを整理し、再利用性を高める強力な機能!

これまでのステップで、サブルーチンや関数を使って処理を部品化する方法を学びました。しかし、プログラムが大規模になってくると、関連する変数、定数、サブルーチン、関数などをまとめて管理したくなりますよね?🤔 そんなときに役立つのがモジュール (MODULE) です!

モジュールは、関連するデータや手続き(サブルーチン、関数)を一つの単位としてまとめるための仕組みです。これにより、プログラムの構造が明確になり、コードの再利用性や保守性が格段に向上します。✨

モジュールの基本的な使い方

モジュールは MODULE 文で始まり、END MODULE 文で終わります。その間に、モジュールに含めたい変数、定数、サブルーチン、関数などを定義します。

モジュールの定義例


MODULE my_constants_module
  IMPLICIT NONE
  ! モジュール内で使用する変数の宣言はここで行う
  REAL, PARAMETER :: PI = 3.14159265359  ! 円周率 π
  REAL, PARAMETER :: GRAVITY = 9.80665    ! 重力加速度 (m/s^2)
END MODULE my_constants_module

MODULE basic_math_module
  IMPLICIT NONE
  ! このモジュールが依存する他のモジュールを指定できる
  ! USE my_constants_module ! 必要であれば

CONTAINS ! モジュール内の手続き(サブルーチン、関数)は CONTAINS 以降に書く

  FUNCTION circle_area(radius) RESULT(area)
    REAL, INTENT(IN) :: radius
    REAL :: area
    ! USE 文でインポートしたモジュールの定数を使える
    ! もし USE 文がなければ、 my_constants_module::PI のように指定する
    REAL, PARAMETER :: PI_local = 3.14159265359 ! またはローカルで定義
    area = PI_local * radius**2
  END FUNCTION circle_area

  SUBROUTINE print_hello()
    PRINT *, "Hello from the module!"
  END SUBROUTINE print_hello

END MODULE basic_math_module
      

モジュールの利用方法 (USE文)

定義したモジュールを利用するには、プログラム本体や他のサブルーチン、関数、あるいは別のモジュールの中で USE 文を使います。USE 文は、通常、プログラム単位の先頭(PROGRAM文やSUBROUTINE文などの直後、IMPLICIT NONEよりも後が一般的)に記述します。


PROGRAM main_program
  ! 利用したいモジュールを USE 文で指定
  USE my_constants_module
  USE basic_math_module
  IMPLICIT NONE

  REAL :: r, a

  ! モジュールで定義された定数を利用
  PRINT *, "円周率 Pi =", PI
  PRINT *, "重力加速度 g =", GRAVITY

  ! モジュールで定義された関数を利用
  r = 5.0
  a = circle_area(r)
  PRINT *, "半径", r, "の円の面積:", a

  ! モジュールで定義されたサブルーチンを利用
  CALL print_hello()

END PROGRAM main_program
      

💡 ポイント: USE 文を使うことで、モジュール内で定義された変数、定数、サブルーチン、関数が、あたかもその場で定義されたかのように利用できるようになります。

モジュールの利点 💪

モジュールを使うことには、たくさんのメリットがあります。

  • コードの再利用性向上: 共通して使う定数や関数などをモジュールにまとめておけば、複数のプログラムやサブルーチンから簡単に呼び出して使えます。同じコードを何度も書く必要がなくなります。
  • プログラムの構造化: 関連する機能をモジュール単位に分割することで、プログラム全体の構造が分かりやすくなります。大規模なプログラムの開発や保守が容易になります。
  • 名前空間の分離: モジュールは独立した名前空間を提供します。これにより、モジュール内で定義した変数名や手続き名が、プログラムの他の部分や別のモジュールで定義された名前と衝突するのを防ぐことができます。
  • インターフェースの明確化: モジュール内の要素(変数、手続きなど)を外部に公開するかどうかを制御できます(後述の PUBLIC / PRIVATE 属性)。これにより、モジュールの使い方を明確にし、意図しない使われ方を防ぐことができます。
  • グローバル変数の代替: 複数のサブルーチンや関数で共通して使いたい変数をモジュール変数として定義することで、グローバル変数のように利用できます。ただし、グローバル変数の多用はプログラムを複雑にする可能性があるため、注意が必要です。モジュールを使うことで、どの変数がどこで使われているか追跡しやすくなります。

PUBLIC属性とPRIVATE属性によるアクセス制御

モジュール内の要素(変数、定数、サブルーチン、関数など)が、モジュールを USE した外部のプログラム単位からアクセスできるかどうかを制御するために、PUBLIC 属性と PRIVATE 属性を使用します。

  • PUBLIC属性: モジュール外部からアクセス可能です。これはデフォルトの属性なので、明示的に指定しない限り PUBLIC になります。
  • PRIVATE属性: モジュール内部からのみアクセス可能です。モジュール外部からはアクセスできません。

これらの属性は、個々の変数や手続きに対して指定することも、モジュール全体に対してデフォルトのアクセスレベルを設定し、一部の要素だけ逆の属性を指定することも可能です。


MODULE access_control_module
  IMPLICIT NONE

  ! デフォルトを PRIVATE に設定 (モジュール内のほとんどを隠蔽したい場合)
  PRIVATE

  ! PUBLIC 属性を明示的に指定したものだけが外部からアクセス可能になる
  PUBLIC :: shared_variable, useful_function

  INTEGER :: shared_variable = 100  ! これは外部からアクセス可能
  REAL :: internal_helper_variable = 0.5 ! これは外部からアクセス不可

CONTAINS

  FUNCTION useful_function(x) RESULT(y) ! これは外部からアクセス可能
    REAL, INTENT(IN) :: x
    REAL :: y
    y = x * internal_helper_variable ! 内部変数は使える
    CALL internal_subroutine()     ! 内部サブルーチンも呼べる
  END FUNCTION useful_function

  SUBROUTINE internal_subroutine() ! これは外部からアクセス不可
    PRINT *, "This is an internal subroutine."
    PRINT *, "Internal helper variable:", internal_helper_variable
  END SUBROUTINE internal_subroutine

END MODULE access_control_module

PROGRAM main_access_test
  USE access_control_module
  IMPLICIT NONE

  REAL :: result

  ! PUBLIC な変数や関数はアクセスできる
  PRINT *, "Shared variable:", shared_variable
  result = useful_function(5.0)
  PRINT *, "Result from useful function:", result

  ! PRIVATE な変数やサブルーチンはアクセスできない (コンパイルエラーになる)
  ! PRINT *, internal_helper_variable
  ! CALL internal_subroutine()

END PROGRAM main_access_test
      

⚠️ 注意: PRIVATE 属性を使うことで、モジュールの内部実装を隠蔽し、外部には必要なインターフェースだけを公開できます。これを「カプセル化」と呼び、ソフトウェア設計において重要な概念です。

モジュールのコンパイル ⚙️

モジュールを使用するプログラムをコンパイルする場合、通常はモジュールを定義しているソースファイルを先にコンパイルする必要があります。コンパイラはモジュールをコンパイルする際に、モジュールの情報を含んだ特別なファイル(gfortran の場合は .mod ファイル)を生成します。そして、そのモジュールを USE しているプログラムをコンパイルする際に、この .mod ファイルを参照します。

例えば、my_modules.f90 というファイルにモジュールが定義されていて、main_program.f90 というファイルがそれを利用している場合、gfortran を使うと以下のようにコンパイルします。


# 1. モジュールを定義しているファイルを先にコンパイル
gfortran -c my_modules.f90

# これにより my_constants_module.mod や basic_math_module.mod といったファイルが生成される

# 2. モジュールを利用するプログラム本体をコンパイル
gfortran -c main_program.f90

# 3. オブジェクトファイルをリンクして実行可能ファイルを作成
gfortran my_modules.o main_program.o -o my_executable

# または、まとめてコンパイル (コンパイラが依存関係を解決してくれる場合がある)
# ただし、ファイル名の順序が重要になることがある
gfortran my_modules.f90 main_program.f90 -o my_executable
      

💡 ヒント: 大規模なプロジェクトでは、Makefile や CMake などのビルドツールを使って、コンパイルの順序や依存関係を自動的に管理するのが一般的です。

まとめ

モジュールは Fortran プログラミングにおいて非常に重要で強力な機能です。関連するデータや手続きをまとめ、コードの再利用性、可読性、保守性を大幅に向上させることができます。特に、チームで開発を行う場合や、大規模なプログラムを作成する場合には必須のテクニックと言えるでしょう。😊

最初は少し難しく感じるかもしれませんが、積極的にモジュールを活用して、整理された効率的な Fortran プログラムを作成するスキルを身につけていきましょう!💪

参考情報

上記の情報は一般的な解説であり、特定のコンパイラや環境によっては挙動が異なる場合があります。詳細については、お使いのコンパイラのマニュアル等をご参照ください。

コメント

タイトルとURLをコピーしました