Fortran/オブジェクト指向プログラミング

出典: フリー教科書『ウィキブックス(Wikibooks)』
Wikipedia
Wikipedia
ウィキペディアFortranの記事があります。

オブジェクト指向プログラミング[編集]

モジュール[編集]

概要[編集]

データはmodule内にまとめることができます。一般的な形式は以下の通りです。

module <name>
    [use <module_names>]
    [<declarations>]
contains
    [<subroutines and functions>]
end module [<name>]

データへのアクセス[編集]

3つのアクセスプロパティがあります: public, private, protected

  • public: 外部のコードから読み書きが可能です。
  • private: 外部のコードからアクセスできません。
  • public, protected: 外部のコードから読み取りが可能です。

他のコードでモジュールを使用する[編集]

モジュールの公開データを外部のコードに含めることができます。 3つの方法があります。

  • use <moduleName>: すべての公開データとメソッドが含まれます
  • use <moduleName>, <renames>: すべての公開データとメソッドが含まれますが、一部の公開データやメソッドの名前を変更します
  • use <moduleName>, only: <subset>: 一部の公開データとメソッドのみが含まれます

[編集]

概要[編集]
module test_m
    implicit none
    private  ! すべてのデータはデフォルトで非公開です。
    ! これらの手続きはpublicに設定され、モジュールの外部からアクセス可能です。
    public print_coords, set_coords 
    real :: x, y  ! モジュールの外部からはアクセスできません。

contains
    subroutine print_coords
        print *, "x, y", x, y
    end subroutine

    subroutine set_coords(new_x, new_y)
        real, intent(in) :: new_x, new_y

        x = new_x
        y = new_y
    end subroutine
end module

program main
    use test_m   ! "test_m"モジュールをインポートします
    implicit none

    call set_coords(1.0, 1.0)  ! test_modからpublicな手続きを呼び出します。
    call print_coords
end program
データアクセス[編集]
module data_access_m
    implicit none
    private
    public a, b
    protected b
    private c
    integer :: a = 1
    integer :: b = 1
    integer :: c = 1
end module

program main
    use data_access_m

    ! publicオブジェクトへのアクセスは可能です。
    print *, a
    ! publicオブジェクトの編集は可能です。
    a = 2
    ! protectedオブジェクトへのアクセスは可能です。
    print *, b
    ! protectedオブジェクトの編集はできません。
    !b = 2 <- エラー
    ! privateオブジェクトへのアクセスはできません
    !print *, c <- エラー
    ! privateオブジェクトの編集はできません
    !c = 2 <- エラー
end program
モジュールの使用[編集]
module test_module
    implicit none
    private
    integer, public :: a = 1
    integer, public, protected :: b = 1
    integer, private :: c = 1
end module test_module

!> test_moduleのすべての公開データをインポートします。
program main
    use test_module

    print *, a, b
end program main

!> すべてのデータをインポートし、名前を変更します。
program main
    use test_module, better_name => a

    ! 新しい名前が利用可能です。
    print *, better_name
    ! 古い名前はもう利用できません。
    !print *, a  <- エラー
end program main

!> 公開データのサブセットのみをインポートします。
program main
    use test_module, only : a

    ! aだけがロードされます。
    print *, a
    ! bはロードされません。
    !print *, b  <- エラー
end program main

サブモジュール[編集]

モジュールはサブモジュールを使用して拡張することができます。複数の利点があります

  • 大きなモジュールの分割
  • インターフェース定義と実装の分割により、依存するモジュールが実装が変更されても再コンパイルする必要がありません
  • 2つのモジュールがお互いからデータを必要とする場合。

[編集]

定義と実装の分割[編集]
!> 円に関する簡単なモジュール
module circle_mod
    implicit none
    private
    public :: area, radius
    real            :: radius
    real, parameter :: PI = 3.1415

    interface  ! インターフェースブロックが必要です。サブモジュール経由で実装される各機能には、ここにエントリが必要です。
        module function area() ! 重要です。"module"キーワードに注意してください。
            real :: area
        end function 
    end interface
end module 

submodule (circle_mod) circle_subm   ! サブモジュール (親モジュール) 子モジュール.
contains
    module function area()  ! 再び "module" キーワード。
        area = PI*radius**2
    end function 
end submodule

program main
    use circle_mod
    implicit none

    radius = 1.0
    print *, "area:", area()
end program

派生データ型[編集]

Fortranでは、他の構造から構造を派生させることができます。これを「派生データ型」と呼び、派生型は親型の特徴を持ち、新たに追加されたものも持ちます。一般的な構文は以下の通りです。

type, extends(<parentTypeName>) :: <newTypeName>
    <definitions>
end type

次の例は、会社内の異なるタイプの人々を示しています。

module company_data_mod
    implicit none
    private
    public phone_type, address_type, person_type, employee_type, salaried_worker_type, hourly_worker_type

    type phone_type
        integer :: area_code, number
    end type

    type address_type
        integer                        :: number
        character (len=:), allocatable :: street, city
        character (len=2)              :: state
        integer                        :: zip_code
    end type

    type person_type
        character (len=:), allocatable :: name
        type (address_type)            :: address
        type (phone_type)              :: phone
        character (len=:), allocatable :: remarks
    end type

    type, extends (person_type) :: employee_type
        integer :: phone_extension, mail_stop, id_number
    end type

    type, extends (employee_type) :: salaried_worker_type
        real :: weekly_salary
    end type

    type, extends (employee_type) :: hourly_worker_type
        real :: hourly_wage, overtime_factor, hours_worked
    end type
end module

program main
    use company_data_mod
    implicit none

    type (hourly_worker_type) :: obj
end program

デストラクタ[編集]

オブジェクトが自動的に削除される前に呼び出される手続きを定義することができます。これはfinalステートメントで行われます。次の例を参照してください。

module person_m
    implicit none

    type person
        integer, allocatable :: numbers(:)
    contains
        final :: del
    end type
contains
    subroutine del(this)
        !! 派生型のデストラクタの例。 allocatables は 
        !! 自動的に解放されるので、これは final の使用法を示すだけです。
        type (person), intent (inout) :: this

        if (allocated(this%numbers)) deallocate (this%numbers)
    end subroutine 
end module

抽象基底型と遅延手続き[編集]

基底型をabstractとして設定することで、その型のオブジェクトを初期化できなくなりますが、その型のサブタイプを派生させることはできます(extendsを使用)。サブタイプで定義する必要のある特定の手続きは、deferredプロパティを持つ必要があり、明示的なインターフェースが必要です。

以下の例はその使用方法を示しています。

module shape_m
    implicit none

    type, abstract :: shape
        real :: a, b
    contains
        procedure :: print => shape_print
        procedure (area_shape), deferred :: area
    end type

    interface
        real function area_shape(this)
            import :: shape
            class (shape), intent (in) :: this
        end function
    end interface
contains
    subroutine shape_print(this)
        class (shape), intent (in) :: this

        print *, 'a,b', this%a, this%b
    end subroutine
end module

module line_m
    use shape_m
    implicit none
    private
    public line

    type, extends (shape) :: line
    contains
        procedure :: area
    end type
contains
    real function area(this)
        class (line), intent (in) :: this

        area = abs(this%a - this%b)
    end function
end module

module rectangle_m
    use shape_m
    implicit none
    private
    public rectangle

    type, extends(shape) :: rectangle
    contains
        procedure :: area
    end type
contains
    real function area(this)
        class (rectangle), intent (in) :: this

        area = this%a * this%b
    end function
end module

program main
    use line_m
    use rectangle_m
    implicit none

    type (line)      :: l
    type (rectangle) :: r

    ! line
    l%a = 2.0
    l%b = 4.0
    print *, "line ...  "
    call l%print
    print *, "-> from:  ", l%a
    print *, "-> to:    ", l%b
    print *, "-> length:", l%area()

    ! rectangle
    r%a = 3.0
    r%b = 5.0
    print *
    print *, "rectangle ..."
    call r%print
    print *, "-> side a:", r%a
    print *, "-> side b:", r%b
    print *, "-> area:  ", r%area()
end program

ポリモーフィックポインタ[編集]

allocate文での型定義とselect type環境を使用して、子クラスへのポインタを作成することができます。

以下の例ではその使用方法を強調しています。

module shape_m
    implicit none

    type, abstract :: shape
        ! 親クラスを実装するために使用される空のクラスです。
        ! abstract の理由: TYPE(!) shapeのオブジェクトは存在しないはずです。
        ! ポリモーフィックCLASSのインスタンスのみ。
    end type
end module

module line_m
    use shape_m
    implicit none

    type, extends (shape) :: line
        ! 1つの属性を持つ子クラスです。
        ! extends(shape)の理由: ポリモーフィックshapeポインタは
        ! この型のオブジェクトを指すことができます。
        real :: length
    end type
end module

module rectangle_m
    use shape_m
    implicit none

    type, extends (shape) :: rectangle
        ! 別の属性を持つ子クラスです
        ! extends(shape)の理由: (lineの説明を参照)
        real :: area
    end type
end module

program main
    use rectangle_m
    use line_m
    implicit none

    class (shape), allocatable :: sh  ! 親クラスへのポインタ。
    ! allocate (line :: sh)
    allocate (rectangle :: sh)  ! 子型を使用して割り当てます
    select type (x => sh)   ! アソシエーションブロック。"x"は子オブジェクトへのポインタおよびその型になります(!!)
        type is (line)      ! 正しい子タイプを選択します (allocateステートメントで使用したもの)
            x%length = 1.0
            print *, 'line length', x%length
        type is (rectangle)
            x%area = 2.0
            print *, 'rectangle area', x%area
        ! class is ()  ! クラスを使用して選択します。
        class default  ! 何も適用されなかった場合。
            error stop 'class/type not specified!'
    end select
end program

用語のまとめ(和訳独自)[編集]

ここでは、Fortranに関連するオブジェクト指向プログラミングの用語をまとめます。

  1. モジュール (Module):
    • データや手続きをまとめた単位。
    • カプセル化や再利用性を促進する。
    • moduleブロックで宣言し、end moduleで終了する。
  2. データアクセスプロパティ:
    • public, private, protectedの3つのアクセスプロパティがある。
    • public: 外部コードから読み書き可能。
    • private: 外部コードからアクセス不可。
    • protected: 外部コードから読み取り可能。
  3. モジュールの使用:
    • 外部のコードでモジュールのデータや手続きを使用する方法。
    • use <moduleName>でモジュールをインポートする。
    • use <moduleName>, only: <subset>で一部のデータや手続きのみをインポートする。
  4. サブモジュール (Submodule):
    • 大規模なモジュールをさらに分割するための仕組み。
    • インターフェースと実装を分離し、依存関係を管理する。
  5. 派生データ型 (Derived Data Type):
    • 他のデータ型から派生した新しいデータ型。
    • type, extends(<parentTypeName>) :: <newTypeName>の形式で宣言する。
  6. デストラクタ (Destructor):
    • オブジェクトが削除される前に実行される手続き。
    • リソースの解放や後処理を行うために使用される。
  7. 抽象基底型 (Abstract Base Type):
    • インスタンスを作成できない抽象的な型。
    • 派生型は抽象基底型から派生して具体的な型を作成する。
  8. 遅延手続き (Deferred Procedure):
    • 派生型で実装する必要のある手続き。
    • 抽象基底型で定義され、具体的な型で実装される。
  9. ポリモーフィックポインタ (Polymorphic Pointer):
    • 異なる型のオブジェクトに対して同じポインタを使用できる仕組み。
    • 実行時に異なる型のオブジェクトを操作するために使用される。

これらの用語を理解することで、Fortranでオブジェクト指向プログラミングをより効果的に行うことができます。