subroutine
やfunction
といった手続きの引数に他の手続きを渡すには,interface
文を書くだけで良い.
external
文という機能もあったそうだが,現在では非推奨.
この記事では,特に「手続きが複数あり,それを引数として渡すことで実行するものを選択したい」という場合に焦点.
(例:ルンゲ・クッタ法をいろいろな関数に適用したい)
例
args1.f90
module mymod
implicit none
contains
subroutine sub1(a, b)
integer, intent(in) :: a, b
write(*, *) 'sub1', a + b
end subroutine sub1
subroutine sub2(a, b)
integer, intent(in) :: a, b
write(*, *) 'sub2', a - b
end subroutine sub2
subroutine wrapper(sub, a, b)
interface ! ここで引数がサブルーチンであることを明示
subroutine sub(a, b)
integer, intent(in) :: a, b
end subroutine sub
end interface ! ここまで
integer, intent(in) :: a, b
call sub(a, b)
end subroutine wrapper
end module mymod
program main
use mymod
implicit none
call sub1(5, 3) ! sub1 8
call sub2(5, 3) ! sub2 2
call wrapper(sub1, 5, 3) ! sub1 8
call wrapper(sub2, 5, 3) ! sub2 2
end program main
引数にする手続きが,異なる個数の引数をとるとき1
1つの解決策は,全ての手続きの引数の数を揃えること.
単体でも動くようにoptional
属性をつけても良いが(例えば下の例のsub1
の引数c
),
sub2
,wrapper
にもoptional
属性をつける必要がある.
args2.f90
module mymod
implicit none
contains
subroutine sub1(a, b, c)
integer, intent(in) :: a, b, c
write(*, *) 'sub1', a + b
end subroutine sub1
subroutine sub2(a, b, c)
integer, intent(in) :: a, b, c
write(*, *) 'sub2', a * b * c
end subroutine sub2
subroutine wrapper(sub, a, b, c)
interface
subroutine sub(a, b, c)
integer, intent(in) :: a, b, c
end subroutine sub
end interface
integer, intent(in) :: a, b, c
call sub(a, b, c)
end subroutine wrapper
end module mymod
program main
use mymod
implicit none
call sub1(5, 3, 2)
call sub2(5, 3, 2)
call wrapper(sub1, 5, 3, 2)
call wrapper(sub2, 5, 3, 2)
end program main
引数にする手続きが,異なる個数の引数をとるとき2
もう1つの解決策は,引数を配列で渡すこと.
但しFortranには配列を展開して手続きに渡すという機能は存在しないため,配列を受けて展開するというためだけの手続きを用意する必要がある(下の例のexe_sub*
).
- 配列のサイズに関する
select
文や,配列を使わずにif (present())
で展開出来なくもないが,実際に呼び出す手続きの間でintent()
属性が異なる場合,より複雑になってしまう-
intent()
属性を削除するという手もあるが,流石にまずいと思う -
exe_sub3(arr_inout, arr_out, arr_in)
のように,intent()
属性に応じて配列を分けることを想定
-
- いっそのこと手続きを引数にせず,文字列で
'sub1'
等と指定してselect
文で分岐させる方が楽?
args3.f90
module mymod
implicit none
contains
subroutine sub1(a, b)
integer, intent(in) :: a, b
write(*, *) 'sub1', a + b
end subroutine sub1
subroutine sub2(a, b, c)
integer, intent(in) :: a, b, c
write(*, *) 'sub2', a * b * c
end subroutine sub2
subroutine exe_sub1(arrin)
integer, intent(in) :: arrin(:)
call sub1(arrin(1), arrin(2))
end subroutine exe_sub1
subroutine exe_sub2(arrin)
integer, intent(in) :: arrin(:)
call sub2(arrin(1), arrin(2), arrin(3))
end subroutine exe_sub2
subroutine wrapper(sub, arrin)
interface
subroutine sub(arrin)
integer, intent(in) :: arrin(:)
end subroutine sub
end interface
integer, intent(in) :: arrin(:)
call sub(arrin(:))
end subroutine wrapper
end module mymod
program main
use mymod
implicit none
call sub1(5, 3)
call sub2(5, 3, 2)
call wrapper(exe_sub1, (/5, 3/))
call wrapper(exe_sub1, (/5, 3, 2/))
call wrapper(exe_sub2, (/5, 3, 2/))
end program main
参考:
あらきけいすけの雑記帳: fortran でサブルーチンの引数にサブルーチンを渡す
stackoverflow: How to pass subroutine names as arguments in Fortran?