Fortran/言語の拡張とオーバーロード

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

手続きのオーバーローディング[編集]

Fortran 90およびそれ以降では、他のいくつかの言語と同様に、渡された引数に基づいてルーチンのリストから適切なルーチンを選択する機能がサポートされています。この選択はコンパイル時に行われるため、実行時のパフォーマンスペナルティはありません。この機能は、モジュールとインターフェースブロックの使用によってアクセスされます。

以下の例では、さまざまな型の引数を処理できるインターフェース関数fを含むモジュールが指定されています。

module extension_m
    implicit none
    private
    public f  ! Only the interface f is accessable outside the module.

    interface f  ! The overloaded function is called "f".
          module procedure f_i  ! "f(x)" for integer argument "x" will call "f_i"
          module procedure f_r  ! "f(x)" for real    argument "x" will call "f_r"
          module procedure f_z  ! ...        complex ....                   "f_z"
   end interface
contains
   integer function f_i(x) result (y)
      integer, intent (in) :: x

      y = x**2 - 1
   end function 

   real function f_r(x) result(y)
      real, intent (in) :: x

      y = x**2 - 1.0
   end function 

   complex function f_z(x) result(y)
      complex, intent (in) :: x

      y = x**2 - 1.0
   end function 
end module

このモジュールを使用するプログラムでは、整数、実数、または複素数型の引数を受け入れる単一のインターフェース関数fにアクセスできます。関数の戻り値の型は入力の型と同じです。このようにして、ルーチンはFortran標準の一部として定義されている多くの組み込み関数と似ています。以下に、例のプログラムが示されています。

program main
    use extension_m
    implicit none
    complex :: xz, yz
    integer :: xi, yi
    real    :: xr, yr

    xi = 2
    xr = 2.0
    xz = 2.0
    yi = f(xi)
    yr = f(xr)
    yz = f(xz)
end program

組み込み関数の拡張[編集]

組み込み関数を拡張することができます。これは演算子のオーバーロードと似ています。

以下では、sqrt関数を拡張してみます。整数型の引数に対しては組み込みの関数が実装されていないことに注意してください。これは、整数型の引数の結果をどのように定義するかが明確でないためです(例えば、ですが、をどのように定義するかがわかりません)。ここでは、結果が常に最も近い整数になるような方法を実装します。

module sqrt_int_m
    implicit none
    private
    public sqrt
    ! use intrinsic sqrt for data types which are not overloaded
    intrinsic :: sqrt
    ! extend sqrt for integers
    interface sqrt
        module procedure sqrt_int
    end interface 
contains
  pure integer function sqrt_int(i)
    integer, intent (in) :: i

    sqrt_int = nint(sqrt(real(i)))
  end function 
end module 

program main
    use sqrt_int_m
    implicit none
    integer :: i

    ! sqrt can be called by real and integer arguments
    do i = 1, 7
        print *, "i, sqrt(i), sqrt(real(i))", i, sqrt(i), sqrt(real(i))
    end do
end program

派生データ型[編集]

Fortran 90およびそれ以降では、既存の型の合成である新しいデータ型の作成がサポートされています。これは配列に似ていますが、構成要素はすべて同じ型である必要はなく、名前で参照されます。このようなデータ型は、その型の変数が宣言される前に宣言され、使用するためにスコープ内にある必要があります。以下は、単純な2次元ベクトル型の例です。

type :: vec_t
   real :: x,y
end type

この型の変数は、変数の特性、ポインター、および次元を含む、他の変数と同様に宣言できます。

type (vec_t) :: a,b
type (vec_t), dimension (10) :: vecs

派生データ型を使用すると、Fortran言語は、プリミティブ型で表されるデータよりも多様な種類のデータを表すことができます。

演算子のオーバーローディング[編集]

演算子をオーバーロードして、派生データ型が標準の操作をサポートし、Fortran言語をネイティブの型とほぼ同じように振る舞う新しい型を持つ可能性を開くことができます。

代入[編集]

代入演算子=をオーバーロードすることができます。 以下の例で示すように、左辺に論理型、右辺に整数を指定した場合の代入方法を定義します。

module overload_assignment_m
    implicit none
    private
    public assignment (=)

    interface assignment (=)
        module procedure logical_gets_integer
    end interface
contains
    subroutine logical_gets_integer(tf, i)
        logical, intent (out) :: tf
        integer, intent (in)  :: i

        tf = (i == 0)
    end subroutine
end module

program main
    use overload_assignment_m
    implicit none
    logical :: tf

    tf = 0
    print *, "tf=0:", tf  ! Yields: T
    tf = 1
    print *, "tf=1:", tf  ! Yields: F
end program

組み込み演算子[編集]

+-*などの組み込み演算子をオーバーロードすることができます。

次の例では、*演算子を論理.and.として機能するようにオーバーロードします。

.

module overload_asterisk_m
    implicit none
    private
    public operator (*)

    interface operator (*)
        module procedure logical_and
    end interface
contains
    pure logical function logical_and(log1, log2)
        logical, intent (in) :: log1, log2

        logical_and = (log1 .and. log2)
    end function
end module

program main
  use overload_asterisk_m
  implicit none
  logical, parameter :: T = .true., F = .false.

  print *, "T*T:", T*T  ! Yields: T
  print *, "T*F:", T*F  ! Yields: F
  print *, "F*T:", F*T  ! Yields: F
  print *, "F*F:", F*F  ! Yields: F
end program

新しい演算子[編集]

新しい独自の演算子を作成することができます。

次の例では、与えられた整数が偶数であるかどうかを出力する単項演算子.even. <int>と、2つのrealベクトルの標準的な外積を行う二項演算子<reals> .cross. <reals>を作成します。

module new_operators_m
    implicit none
    private
    public operator (.even.)
    public operator (.cross.)

    interface operator (.even.)
        module procedure check_even
    end interface

    interface operator (.cross.)
        module procedure cross_product
    end interface
contains
    pure logical function check_even(i)
        integer, intent (in) :: i

        check_even = (modulo(i, 2) == 0)
    end function

    function cross_product(x, y) result(z)
        real, intent (in) :: x(3), y(3)
        real :: z(3)

        z(1) = x(2)*y(3) - x(3)*y(2)
        z(2) = x(3)*y(1) - x(1)*y(3)
        z(3) = x(1)*y(2) - x(2)*y(1)
    end function
end module

program main
    use new_operators_m
    implicit none
    integer :: i
    real    :: x(3), y(3)

    do i = 1, 6
        print *, "i:", i, "even?", .even. i
    end do
    print *

    x = [ 1, 2,  3]
    y = [-1, 2, -3]
    print *, 'x', x
    print *, 'y', y
    print *, 'x cross_product y', x .cross. y
end program