2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Fortran】サブルーチンの引数にサブルーチンを渡す

Last updated at Posted at 2019-07-30

subroutinefunctionといった手続きの引数に他の手続きを渡すには,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),
sub2wrapperにも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?

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?