概要
Fortranのtemplateは,先の記事から結構変化していて,私の要望に近づいています.
FortranのGenericsの現状
先の記事で,FortranのGenerics(主にtemplate)について紹介し,自身の要望を述べました.
その後,Genericsを策定するチームのメンバーがtemplateの現状について説明している動画を紹介していただいたので,現状を改めて紹介まします.
axpy
動画で説明されていたaxpyの例を紹介します.
axpyは,$$y = ax + y$$を計算する手続の総称です.二つのベクトルx, y
を受け取り,x
をスカラ倍(factorがa
)した結果をy
に加算します.Fortranでは,簡単に
subroutine axpy(a, x, y)
real, intent(in) :: a
real, intent(in) :: x(:)
real, intent(inout) :: y(:)
y = y + a*x
end subroutine
と書けます.これは単精度浮動小数点数に対するaxpyなので,single-precisionのsを付けてsaxpyと呼ばれます.単精度以外にも,倍精度浮動小数点数に対するaxpy(daxpy)もあります.
現在検討中のtemplateでは,saxpyとdaxpyはまとめて次のように書ける(ようになる)そうです.
template axpy_tmpl(k)
private
public :: axpy
integer, constant :: k
interface axpy
procedure axpy_
end interface
constants
subroutine axpy_(a, x, y)
real(k), intent(in) :: a
real(k), intent(in) :: x(:)
real(k), intent(inout) :: y(:)
y = y + a*x
end subroutine
end template
テンプレートをインスタンス化し,呼び出すには下記のようにします.
integer, parameter :: sp = kind(1.0), dp = kind(1.d0)
instantiate axpy_tmpl(sp)
instantiate axpy_tmpl(dp)
real(sp) :: a, x(10), y(10)
real(dp) :: da, dx(10), dy(10)
call axpy(a, x, y)
call axpy(da, dx, dy)
型の種別を表す定数integer, constant :: k
は,parameterized derived typeであったinteger, kind
の拡張と見なせるでしょう.これは実数型の種別を切り替えるだけですが,requirement
の要求が不要であること,インスタンス化した際に同じ名前が利用でき,引数の型(種別)で呼び出される手続が切り替えられていることは,非常に素晴らしいことだと思います.(interface
宣言なしでそれが可能ならなお素晴らしいのですが…)
次に,任意の型を引数に取れるように発展させます.任意の型を引数に取り,axpyを計算するには,加算と乗算を行う演算子が定義されていなければなりません.任意の型に対応するには,先の記事で紹介した通りにrequirement
が必要なようです.また,演算を行う手続のインタフェースも必要です.
型パラメータT
は,これまでのtype T; end type
(空のユーザ定義派生型定義)から,type, deferred :: T
という宣言に変わっています.deferred
は,現状の仕様では遅延宣言とよばれるオブジェクト指向プログラミングの機能で,C++のvirtual
(仮想関数の定義)に対応しています.それを変数に拡張しようとしているのでしょう.これはよい拡張だと思います.
requirement bin_op(T, op)
type, deferred :: T
elemental function op(a, b)
type(T), intent(in) :: a, b
type(T) :: op
end function
end requirement
関数テンプレートでは,任意の型に対する加算と乗算の抽象インタフェース(のようなものを)requires
で記述します.
template axpy_tmpl(T, plus, times)
private
public :: axpy
requires bin_op(T, plus)
requires bin_op(T, times)
interface axpy
procedure axpy_
end interface
constants
subroutine axpy_(a, x, y)
type(T), intent(in) :: a
type(T), intent(in) :: x(:)
type(T), intent(inout) :: y(:)
y = plus(y, times(a, x))
end subroutine
end template
これで,加算や演算が定義されていない型T
が与えられたときの問題は排除できます.先の記事では,既に演算子が定義されている組込型に対しても,いちいち加算や乗算を書かなければならないなら総称名でよいと書きました.現状では,インスタンス化の際にオペレータは必要になるものの,既定のオペレータを渡すことでその問題を回避しているようです.
integer, parameter :: sp = kind(1.0), dp = kind(1.d0)
instantiate axpy_tmpl(real(sp), operator(+), operator(*))
instantiate axpy_tmpl(real(dp), operator(+), operator(*))
instantiate axpy_tmpl(integer, operator(+), operator(*))
real(sp) :: a, x(10), y(10)
real(dp) :: da, dx(10), dy(10)
integer :: ia, ix(10), iy(10)
call axpy(a, x, y)
call axpy(da, dx, dy)
call axpy(ia, ix, iy)
異なる型同士の演算については,上記の例を素直に拡張して,
requirement bin_op(T, U, V, op)
type, deferred :: T, U, V
elemental function op(a, b)
type(T), intent(in) :: a
type(U), intent(in) :: b
type(V) :: op
end function
end requirement
template axpy_tmpl(T, U, V, plus, times)
private
public :: axpy
requires bin_op(T, U, V, plus)
requires bin_op(T, U, V, times)
interface axpy
procedure axpy_
end interface
constants
subroutine axpy_(a, x, y)
type(T), intent(in) :: a
type(U), intent(in) :: x(:)
type(V), intent(inout) :: y(:)
y = plus(y, times(a, x))
end subroutine
end template
とし,インスタンス化の際に適切な型を当てはめます.
integer, parameter :: sp = kind(1.0), dp = kind(1.d0)
instantiate axpy_tmpl(real(sp), integer, real(dp), operator(+), operator(*))
real(sp) :: a
real(dp) :: dy(10)
integer :: ix(10)
call axpy(a, ix, dy)
異なる型間の加算や乗算は定義していませんが,組込型であれば,Fortranに備わっている機能(暗黙の型変換)で実行できるということでしょう.
例に対する私の感想
先の記事と比較すると,私が希望していた内容に近くなってきていると思います.急にどうした…?
しかし,相変わらず記述量が多いのは気になります.requirement
とrequires
,関数テンプレート内のinterface
等々.組込型の種別を変えるだけの例ではrequirement
が必要ではなかったのですから,既定の組込型と演算に限ってrequirement
が不要とかはできないものでしょうか.type, intrinsic, deferred :: T
とかで識別自体はできる気がします.character
やlogical
が入ってきたときのことを考えると,完璧ではないでしょうが…
interface
は,インスタンス化した関数テンプレートに同じ名前を使うために必要なのだと推察します.interface
を書くか別名を与えるかになるのでしょうか?
まとめ
かなり良くなっていました.Fortranは記述量が多いため,なるべく記述を簡略化できる方向で改善していってもらえるとうれしいと思います.あとは,スカラ変数と配列,あるいは配列次元をどうテンプレート化できるようになるのか気になります.