Fortran
オブジェクト指向
StackOverflow
fortran2008
ModernFortran

Fortranで(a==1 && a==2 && a==3)を真にする(別解)

概要

(a ==1 && a== 2 && a==3)が真になるようなプログラムを,静的変数を使って実装したので投稿します.
Fortranの静的変数の宣言に驚くことと思います.

背景

前回の記事において,StackOverFlowで話題になっていた(a ==1 && a== 2 && a==3)が真になるようなプログラムをFortranで作りました.Fortranの規格あるいはコンパイラの実装の穴をつくような方法でしたので,別解として,穴をつかない方法で実装しました.この実装には静的変数を使いますが,Fortranでは静的変数の宣言の方法がC言語系統の言語とは異なります.配列が1始まりであるとか列優先であるとかで移植の際に色々と論争を巻き起こしますが,この静的変数の方が奇妙だと思っています.

実装

前回の記事と同じく派生型StrangeIntを使い,比較演算子==をオーバーロードします.比較演算を実行する型束縛手続Equals内では,正直にStrangeIntの値を変化させるのではなく,静的変数を宣言してその値と右辺値を比較するようにします.細かい実装については前回の記事を参考にしてください.

class_StrangeInt.f90
module class_StrangeInt
    implicit none
    private

    type,public :: StrangeInt
        integer :: value

        contains

        procedure,private,pass :: Equals
        generic :: operator (==) => Equals
    end type StrangeInt

    contains

    function Equals(this,criterion) result(isEqual)
        implicit none
        class(StrangeInt),intent(in) :: this
        integer,intent(in) :: criterion

        logical :: isEqual
        integer :: i = 0

        i = i + 1
        isEqual = (i == criterion)
    end function Equals
 end module class_StrangeInt

戻り値を格納する論理型変数isEqualの下に整数型変数iを宣言し,1を足し,(i == criterion)で右辺値と比較した結果を返します.特に難しいことをしているわけではありませんが,C言語系統に習熟している人が見ると,おや?と思うのではないでしょうか.

Fortranの静的変数

おや?と思うのはiの変数宣言の所だと思います.C言語では,型名だけで宣言すると自動変数となり,変数が存在している関数が終了すると自動で解放されます.そして,宣言時に代入をしていると,実行毎にその値で初期化されます.関数を抜けても値を保持するようにするには,staticを付けて静的変数として宣言する必要があります.つまり,

    int a = 0; //自動変数.寿命は関数が終わるまで.呼び出される度に0で初期化される.
    static int a = 0; //静的変数.寿命はプログラムが終わるまで.0で初期化されるのは1度だけ.

という違いがあります.

Fortranでは,関数・サブルーチン内で変数宣言時に値を代入すると,その変数は静的変数になります.つまり,

    integer :: a = 0 !静的変数.寿命はプログラムが終わるまで.0で初期化されるのは1度だけ.
    integer :: a !自動変数.寿命は関数が終わるまで.
    a = 0        !関数が呼び出される度に0で初期化される.

ということです.

複数の言語をさわる人は知っておいた方がよい知識でしょう.
ちなみに,Fotranには明示的に静的変数を宣言するsave属性があるので,

  • 基本的には変数宣言時に値を代入しない
  • save属性を付与して静的変数である事を明示する場合のみ,宣言時に値を代入する

というルールを設けた方がよいと考えています.

動作テスト

前回の記事と同じメインルーチンを書いて実行してみましょう.

main.f90
program main
    use class_StrangeInt
    implicit none

    type(StrangeInt) :: a

    if(a==1 .and. a==2 .and. a==3)then
        print *,"TRUE"
    end if
end program main

StrangeInt型の変数aの値は全く参照しないので,代入すらしていません.
実行すると

 TRUE

と表示され,この方法でも(a==1 .and. a==2 .and. a==3)を真にすることができました.

まとめ

前回の記事に引き続き,(a==1 .and. a==2 .and. a==3)が真になるようなプログラムのFortranで実装しました.派生型を宣言して比較演算子==をオーバーロードし,比較演算を実行する型束縛手続内で静的変数と右辺値を比較するようにしました.
併せてFortranではローカル変数の宣言時に値を代入すると静的変数になるということも説明しました.古いFORTRAN/Fortranプログラムを他言語に移植しなければならず,四苦八苦している人の一助になれば幸いです.