fortran66のブログ

fortran について書きます。

【メモ帳】Fortran 2018 の配列 reduce (汎)関数

配列 reduction

Fortran 90 で配列 reduction 関数として、sum(), product() 等が与えられましたが、Fortran 2018 規格ではユーザー定義の reduction 演算を行う reduce 汎関数が導入されました。ここで配列 reduction とは演算の結果として、配列の次元 rank が減るような演算を行う操作を指します。

引数としては、被演算配列と結合則を満たす pure な2項演算関数を取ります。reduce 汎関数と書いてみたのは、関数を引数として取って数値を返り値として返すからです。(まぁ Fortran 文法上は、関数と書いてもばちは当たらないと思います。)

なお、ここでいう結合則は数学的な形式上の結合則で、浮動小数点数では厳密にいえば実数で成り立つ様々な結合則が成り立たたないわけですが、それは気にしなくていいそうです。

最近無料化された intel fortranFortran 2018 に完全準拠したと嘯いているので、以下で試してみます。

実行例

一例として積の modulo 演算を取るものを以下に示します。modulo 演算は最後に一回だけ余りをとっても、途中で演算ごとに余りをとっても結果は変わらないはずですが、積の途中で overflow すると困るので積を取るごとに余りを取りたいことがあります。reduce 汎関数を使うと、これを簡潔に書けます。

ソース・プログラム

規格では pure 関数は module (大域)変数に依存しても良いです。ただし、演算中にその module 変数が書き換えられてはならない条件付きで。

    module test_m 
        implicit none
        real :: r = 60.0
    contains
        pure real function f(a, b) result(res)
            real, intent(in) :: a, b
            res = mod(a * b, r)
        end function f
    end module test_m
    
    program F2018
        use test_m
        implicit none
        print *, reduce([9999.,1213.,33333.], f)                         !Fortran 2018
        print *, mod(mod(9999. * 1213., 60.0) * 3333., 60.0) 
        print *, mod(product([9999.,1213.,33333.]), 60.0)
        
        stop 999, quiet = .true.            !Fortran 2018
    end program F2018

実行結果

三番目の結果が一致しないのは、単精度仮数部が overflow して下の方の桁が切り捨てられたためだと思います。 想定通りの結果が得られたのではないかと思います。

   51.00000
   51.00000
   36.00000