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