派生型#

変数で前述したように、Fortranには5つの組み込みデータ型があります。 _派生型_は、他の組み込み型と他の派生型をカプセル化できる特別な形式のデータ型です。 CおよびC ++プログラミング言語の_struct_と同等と見なすことができます。

派生型の概要#

基本的な派生型の例を次に示します

type :: t_pair
  integer :: i
  real :: x
end type

t_pair型の変数を作成し、そのメンバーにアクセスするための構文は次のとおりです。

! Declare
type(t_pair) :: pair
! Initialize
pair%i = 1
pair%x = 0.5

パーセント記号は、派生型のメンバーにアクセスするために使用されます。

上記のコードスニペットでは、派生型のインスタンスを宣言し、そのメンバーを明示的に初期化しました。派生型コンストラクターを呼び出すことによって、派生型メンバーを初期化することもできます。

派生型コンストラクターを使用した例

pair = t_pair(1, 0.5)      ! Initialize with positional arguments
pair = t_pair(i=1, x=0.5)  ! Initialize with keyword arguments
pair = t_pair(x=0.5, i=1)  ! Keyword arguments can go in any order

デフォルト初期化を使用した例

type :: t_pair
  integer :: i = 1
  real :: x = 0.5
end type

type(t_pair) :: pair
pair = t_pair()       ! pair%i is 1, pair%x is 0.5
pair = t_pair(i=2)    ! pair%i is 2, pair%x is 0.5
pair = t_pair(x=2.7)  ! pair%i is 1, pair%x is 2.7

派生型の詳細#

すべてのオプションのプロパティを含む派生型の完全な構文を以下に示します

type [,attribute-list] :: name [(parameterized-declaration-list)]
  [parameterized-definition-statements]
  [private statement or sequence statement]
  [member-variables]
contains
  [type-bound-procedures]
end type

派生型を宣言するためのオプション#

attribute-listは以下を指します

  • publicまたはprivateのいずれかである_アクセス-型_

  • bind(c) Cプログラミング言語との相互運用性を提供します

  • extends(_親_ )。ここで、_親_は、現在の派生型がすべてのメンバーと機能を継承する、以前に宣言された派生型の名前です。

  • abstract – 高度なプログラミングチュートリアルで説明されているオブジェクト指向機能

属性bind(c)またはステートメントsequenceが使用されている場合、派生型は属性extendsを持つことができず、その逆も同様です。

sequence属性は、以下のメンバーが派生型内で定義されているのと同じ順序でアクセスされる必要があることを宣言するためにのみ使用できます。

sequenceを使用した例

type :: t_pair
  sequence
  integer :: i
  real :: x
end type
! Initialize
type(t_pair) :: pair
pair = t_pair(1, 0.5)

ステートメントsequenceを使用すると、以下に定義されているデータ型がallocatable型でもpointer型でもないことが前提となります。さらに、これらのデータ型がメモリに特定の形式で格納されることを意味するものではありません。つまり、contiguous属性との関係はありません。

_アクセス-型_属性publicprivateは、使用されている場合、以下に宣言されているすべてのメンバー変数に属性が自動的に割り当てられることを宣言します。

属性bind(c)は、Fortranの派生型とCの構造体の互換性を実現するために使用されます。

bind(c)を使用した例

module f_to_c
  use iso_c_bindings, only: c_int
  implicit none

  type, bind(c) :: f_type
    integer(c_int) :: i
  end type

end module f_to_c

次のC構造体型と一致します

struct c_struct {
  int i;
};

属性bind(c)を持つFortran派生型は、sequence属性とextends属性を持つことができません。さらに、Fortranのpointer型またはallocatable型を含めることはできません。

parameterized-declaration-listはオプションの機能です。使用されている場合、パラメータは[parameterized-definition-statements]の代わりにリストする必要があり、lenパラメータまたはkindパラメータ、またはその両方である必要があります。

parameterized-declaration-listと属性publicを使用した派生型の例

module m_matrix
 implicit none
 private

 type, public :: t_matrix(rows, cols, k)
   integer, len :: rows, cols
   integer, kind :: k = kind(0.0)
   real(kind=k), dimension(rows, cols) :: values
 end type

end module m_matrix

program test_matrix
 use m_matrix
 implicit none

 type(t_matrix(rows=5, cols=5)) :: m

end program test_matrix

この例では、パラメータkにはすでにデフォルト値kind(0.0)(単精度浮動小数点数)が割り当てられています。したがって、メインプログラム内の宣言のように、省略できます。

デフォルトでは、派生型とそのメンバーはパブリックです。ただし、この例では、属性privateがモジュールの先頭で使用されています。したがって、モジュール内のすべては、明示的にpublicとして宣言されない限り、デフォルトでprivateになります。上記の例で型t_matrixに属性publicが指定されていない場合、コンパイラはprogram test内でエラーをスローします。

属性extendsはF2003標準で追加され、オブジェクト指向パラダイム(OOP)の重要な機能、つまり継承を導入します。子タイプが拡張可能な親タイプから派生できるようにすることで、コードの再利用性を可能にします:type, extends(parent) :: child。ここで、childtype :: parentからすべてのメンバーと機能を継承します。

属性 extends を使用した例

module m_employee
  implicit none
  private
  public t_date, t_address, t_person, t_employee
  ! Note another way of using the public attribute:
  ! gathering all public data types in one place.

  type :: t_date
    integer :: year, month, day
  end type

  type :: t_address
    character(len=:), allocatable :: city, road_name
    integer :: house_number
  end type

  type, extends(t_address) :: t_person
    character(len=:), allocatable :: first_name, last_name, e_mail
  end type

  type, extends(t_person)  :: t_employee
    type(t_date) :: hired_date
    character(len=:), allocatable :: position
    real :: monthly_salary
  end type

end module m_employee

program test_employee
  use m_employee
  implicit none
  type(t_employee) :: employee

  ! Initialization

  ! t_employee has access to type(t_date) members not because of extends
  ! but because a type(t_date) was declared within t_employee.
  employee%hired_date%year  = 2020
  employee%hired_date%month = 1
  employee%hired_date%day   = 20

  ! t_employee has access to t_person, and inherits its members due to extends.
  employee%first_name = 'John'
  employee%last_name  = 'Doe'

  ! t_employee has access to t_address, because it inherits from t_person,
  ! which in return inherits from t_address.
  employee%city         = 'London'
  employee%road_name    = 'BigBen'
  employee%house_number = 1

  ! t_employee has access to its defined members.
  employee%position       = 'Intern'
  employee%monthly_salary = 0.0

end program test_employee

派生型のメンバーを宣言するオプション#

[member-variables] は、すべてのメンバーデータ型の宣言を指します。これらのデータ型は、上記の例ですでに示されているように、任意の組み込みデータ型、および/または他の派生型にすることができます。ただし、メンバー変数には、type [,member-attributes] :: name[attr-dependent-spec][init] の形式で、独自の広範な構文を使用できます。

type: 任意の組み込み型または他の派生型

member-attributes (オプション)

  • public または private アクセス属性

  • protected アクセス属性

  • allocatable dimension の有無にかかわらず、動的配列を指定します

  • pointer, codimension, contiguous, volatile, asynchronous

一般的なケースの例

type :: t_example
  ! 1st case: simple built-in type with access attribute and [init]
  integer, private :: i = 0
  ! private hides it from use outside of the t_example's scope.
  ! The default initialization [=0] is the [init] part.

  ! 2nd case: protected
  integer, protected :: i
  ! In contrary to private, protected allows access to i assigned value outside of t_example
  ! but is not definable, i.e. a value may be assigned to i only within t_example.

  ! 3rd case: dynamic 1-D array
  real, allocatable, dimension(:) :: x
  ! the same as
  real, allocatable :: x(:)
  ! This parentheses' usage implies dimension(:) and is one of the possible [attr-dependent-spec].
end type

以下の属性:pointercodimensioncontiguousvolatileasynchronous は、クイックスタート チュートリアルでは扱わない高度な機能です。ただし、読者がこれらの機能が存在することを知り、認識できるように、ここに示します。これらの機能は、今後の 上級プログラミング ミニブックで詳しく説明します。

型結合手続き#

派生型には、それに *結合* された関数またはサブルーチンを含めることができます。これらを *型結合手続き* と呼びます。型結合手続きは、すべてのメンバー変数宣言の後に続く contains 文に従います。

現代のFortranのOOP機能を掘り下げずに、型結合手続きを完全に説明することは不可能です。ここでは、基本的な使用方法を示す簡単な例に焦点を当てます。

基本的な型結合手続きを持つ派生型の例を次に示します

module m_shapes
  implicit none
  private
  public t_square

  type :: t_square
  real :: side
  contains
    procedure :: area  ! procedure declaration
  end type

contains

  ! Procedure definition
  real function area(self) result(res)
    class(t_square), intent(in) :: self
    res = self%side**2
  end function

end module m_shapes

program main
  use m_shapes
  implicit none

  ! Variables' declaration
  type(t_square) :: sq
  real :: x, side

  ! Variables' initialization
  side = 0.5
  sq%side = side

  x = sq%area()
  ! self does not appear here, it has been passed implicitly

  ! Do stuff with x...

end program main

新しい点

  • self は、型結合関数内で派生型 t_square のインスタンスを表すために選択した任意の名前です。これにより、メンバーにアクセスし、型結合手続きを呼び出すときに引数として自動的に渡すことができます。

  • area 関数のインターフェースでは、type(t_square) の代わりに class(t_square) を使用しています。これにより、t_square を拡張する派生型で area 関数を呼び出すことができます。キーワード class は、OOP機能のポリモーフィズムを導入します。

上記の例では、型結合手続き area は関数として定義されており、x = sq%area()print *, sq%area() などの式でのみ呼び出すことができます。代わりにサブルーチンとして定義すると、独自の call 文から呼び出すことができます

! Change within module
contains
  subroutine area(self, x)
    class(t_square), intent(in) :: self
    real, intent(out) :: x
    x = self%side**2
  end subroutine

! ...

! Change within main program
call sq%area(x)

! Do stuff with x...

型結合関数の例とは対照的に、ここでは2つの引数があります

  • class(t_square), intent(in) :: self – 派生型自体のインスタンス

  • real, intent(out) :: x – 計算された面積を格納し、呼び出し元に返すために使用されます