LoginSignup
8
3

More than 3 years have passed since last update.

複数の引数を持つ関数を一つの引数の関数として関数等から呼びたいin Fortran

Posted at

Fortranで、一変数関数f(x)を引数とする関数g(f)があった時、このgの中にf2(x,y)のyを固定したものf2(x,10)等を入れる方法についてです。多分Fortran2008からできるらしいです。
https://stackoverflow.com/questions/31208594/how-to-pass-a-function-with-multiple-arguments-to-a-subroutine-that-expects-a-fu/31208717#31208717

これを応用すると、オブジェクト指向で作ったオブジェクトに付随する関数も引数として呼べるようになります。

バージョン

gfortran: gcc version 8.2.0 (Homebrew GCC 8.2.0)

コード

Fortranで以下のmoduleを考えます。

module test
    contains
    subroutine tasu(a)
        implicit none
        real(8),intent(in)::a
        write(*,*) "a+10=",a+10 
    end subroutine tasu

    subroutine kakeru(a)
        implicit none
        real(8),intent(in)::a
        write(*,*) "a*10=",a*10
    end subroutine kakeru

    subroutine keisan(tasuorkakeru)
        interface 
            subroutine tasuorkakeru(a)
                real(8),intent(in)::a
            end subroutine
        end interface
        real(8)::a,b

        a = 100
        call tasuorkakeru(a)

        a = 1000
        call tasuorkakeru(a)

    end subroutine keisan

このmoduleのkeisanというsubroutineは、引数に関数を入れることができます。
例えば、

program main
    use test
    implicit none

    call keisan(tasu)
    call keisan(kakeru)    

end program
 a+10=   110.00000000000000     
 a+10=   1010.0000000000000     
 a*10=   1000.0000000000000     
 a*10=   10000.000000000000  

というような形で、関数を切り替えて使えるわけです。
ここで入れられる関数は一変数関数だけです。なので、

    subroutine tasu_a(a,b)
        implicit none
        real(8),intent(in)::a,b
        write(*,*) "a+b=",a+b
    end subroutine tasu_a

という関数は

call keisan(tasu_a)

と入れることはできません。
しかし、bの値を固定してxの関数として引数に使いたいこともあるかと思います。

そのような場合には、
containsを使って中にラッパー関数を定義すると、bの値を固定した関数を作ることができます。
つまり、

module test
    contains
    subroutine tasu(a)
        implicit none
        real(8),intent(in)::a
        write(*,*) "a+10=",a+10 
    end subroutine tasu

    subroutine kakeru(a)
        implicit none
        real(8),intent(in)::a
        write(*,*) "a*10=",a*10
    end subroutine kakeru

    subroutine tasu_a(a,b)
        implicit none
        real(8),intent(in)::a,b
        write(*,*) "a+b=",a+b
    end subroutine tasu_a

    subroutine keisan(tasuorkakeru)
        interface 
            subroutine tasuorkakeru(a)
                real(8),intent(in)::a
            end subroutine
        end interface
        real(8)::a,b

        a = 100
        call tasuorkakeru(a)

        a = 1000
        call tasuorkakeru(a)

    end subroutine keisan
end module test

program main
    use test
    implicit none
    real(8)::b

    call keisan(tasu)
    call keisan(kakeru)

    b = 100d0
    call keisan(wrapper)
    b = 20d0
    call keisan(wrapper)

    contains
    subroutine wrapper(a)
        real(8),intent(in)::a
        call tasu_a(a,b)
    end subroutine

end program

とすれば、出力は

 a+10=   110.00000000000000     
 a+10=   1010.0000000000000     
 a*10=   1000.0000000000000     
 a*10=   10000.000000000000     
 a+b=   200.00000000000000     
 a+b=   1100.0000000000000     
 a+b=   120.00000000000000     
 a+b=   1020.0000000000000   

となり、ちゃんとbを固定した関数ができていることがわかります。

オブジェクトに付随する関数を引数としたい

以下のようなFortran2003以降でできるようになったオブジェクト指向なFortranコードを用意しました。

module test2
    implicit none
    type testtype
        real(8)::a
        contains
        procedure::plus => plus1
    end type testtype

    contains

    subroutine plus1(self,a,b)
        class(testtype)::self
        real(8),intent(in)::a
        real(8),intent(out)::b
        write(*,*) "self%a",self%a
        b = self%a*2+a
        write(*,*) "plus1"
        return 
    end subroutine

    subroutine plus0(a,b)
        real(8),intent(in)::a
        real(8),intent(out)::b
        b = a*2
        write(*,*) "plus0"
    end subroutine

    subroutine matome(func)
        interface 
            subroutine func(a,b)
                implicit none
                real(8),intent(in)::a
                real(8),intent(out)::b
            end 
        end interface
        real(8)::a,b
        a = 10d0
        b = 0d0
        call func(a,b)
        write(*,*) b
    end subroutine
end module

ここで定義したplus1(self,a,b)はtype testtypeに付随するようにしたので、

program main
    use test2
    implicit none
    type(testtype)::kk
    real(8)::a,b

    kk%a = 5d0
    a = 15d0
    call kk%plus(a,b)
    write(*,*) b

    call plus0(a,b)
    write(*,*) b
end program main
 self%a   5.0000000000000000     
 plus1
   25.000000000000000     
 plus0
   30.000000000000000     
 plus0
   20.000000000000000 

のように、type testtypeの変数kkのsubroutineとしてkk%plus(a,b)で呼べるようになっています。
この引数は見た目二つなので、plus0(a,b)を引数にしたkeisan(plus0)のように、keisan(kk%plus)とできそうに見えます。

call matome(kk%plus)

しかし、これは

     call matome(kk%plus)
                       1
Error: Expected argument list at (1)

というエラーが出てしまってコンパイルできません。

これではオブジェクトに付随した関数は引数にできません。
これを解決するには、


program main
    use test2
    implicit none
    type(testtype)::kk
    real(8)::a,b

    kk%a = 5d0
    a = 15d0
    call kk%plus(a,b)
    write(*,*) b


    call plus0(a,b)
    write(*,*) b

    call matome(plus0)
    call matome(wrapper)

    kk%a = 25d0
    call matome(wrapper)

    contains

    subroutine wrapper(a,b)
        real(8),intent(in)::a
        real(8),intent(out)::b
        call kk%plus(a,b)
        return
    end subroutine

end program main
 self%a   5.0000000000000000     
 plus1
   25.000000000000000     
 plus0
   30.000000000000000     
 plus0
   20.000000000000000     
 self%a   5.0000000000000000     
 plus1
   20.000000000000000     
 self%a   25.000000000000000     
 plus1
   60.000000000000000  

のようにラッパー関数をつけてやればよいです。これによって、testtypeの中身が変わればちゃんと値が変わるようになりました。

8
3
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
8
3