古い Fortran プログラムを現代的な形式に書き換える

本記事はnAG技術者による技術エッセイです。
(尚、nAG Fortranコンサルティング ではFortranプログラムの現代化書き換えサービスも行っております。)

最近は Fortran を学んだとしても、選択戻り(ALTERNATE RETURN)や共通ブロック(COMMON BLOCK)を知る人は多くは無いでしょう。ですが、古いプログラムを'現代化'するためにはこれらの構文を正確に知る必要があります。

選択戻りは比較的簡単です。例えば以下の様に利用されます:

      SUBROUTINE MYSQRT(A,*,*)
      REAL A
      IF (A.LT.0.0) RETURN 1
      IF (A.LT.1.0) RETURN 2
      A = SQRT(A)
      RETURN
      END

これは例えば以下のプログラムから呼ばれます:

      PROGRAM MAIN
      REAL A
      EXTERNAL MYSQRT
      
      READ(5,800) A
 800  FORMAT(1E20.10)
      CALL MYSQRT(A,*100,*200)
      WRITE(6,900) A
      GOTO 700
 900  FORMAT (19HTHE SQUARE ROOT IS ,1E20.5)
 100  WRITE(6,901)
 901  FORMAT(14H A IS NEGATIVE)
      GOTO 700
 200  WRITE(6,902)
 902  FORMAT(17H A IS LESS THAN 1)
 700  CONTINUE
      END

選択戻りは、MYSQRT の呼出し側の引数をラベル、および MYSQRT 内では RETURN1 と RETURN2 を用いて、対応する引数で指定された呼出し側プログラムのラベルにジャンプします。この構文は現代的な Fortran では Obsolescent 仕様なので、簡単に一つの整数パラメータを設定して終了時にこれをチェックするな処理に変更した方が賢明です。これは古い構文について知っていれば容易でしょう。

しかし共通ブロックの方がより複雑です。これは古いプログラムに遍在しており、ルーチンのパラメータリスト以外にルーチン間で情報をやりとりする手段として使われてきました。また、希少なメモリ空間を再利用する方法として使用されているかもしれません。

無名共通ブロック(BLANK COMMON)は名前が無く、以下の様に記述されます:

       REAL(100) X,Y
       COMMON X, Y 

メインプログラムは以下のようなものです:

      PROGRAM DKS
      INTEGER I
      REAL X(5),Y(5)
      COMMON X,Y
      EXTERNAL MYSUB
      
      DO 10 I=1,5
       X(I) = FLOAT(I)+ 0.5
       Y(I) = X(I)**2
 10   CONTINUE
      WRITE(6,900) Y
 900  FORMAT(10H VALUES = , 5F20.5)
      CALL MYSUB()
      END

そしてサブルーチンでは、COMMON の実数と整数が同じ領域を占める事という性質を利用することが出来ます:

      SUBROUTINE MYSUB()
      INTEGER IY(6),I
      REAL Z(4)
      COMMON Z,IY
      WRITE(6,900) Z
 900  FORMAT(10H VALUES = , 4F20.5)
      DO 10 I=1,6
        IY(I) = I
 10   CONTINUE
      WRITE(6,901) IY
 901  FORMAT(10H VALUES = , 6I10)  
      RETURN
      END

こうしたその値は記憶列結合により結ばれています。例えば Z(4) の値は無名共通ブロックの4番目の位置にある X(4) から得られます。このようなやり方は強力ですがエラーが発生しやすいことは明らかです。

このことから名前付き共通ブロックの議論の基礎が築かれました。以下の人工的なプログラムはその使用法を示しています:

      PROGRAM DKSPROG
      REAL X(5),Y(5)
      COMMON /DKS/ X
      COMMON /DKS/ Y
C     EXTENDS THE LABELLED COMMON BLOCK - 10 BASIC STORAGE UNITS
      EXTERNAL MYSUB1, MYSUB2
 
      CALL MYSUB1()
      CALL MYSUB2()
      END
      
      SUBROUTINE MYSUB1()
      INTEGER IY(6)
      REAL Z(4)
      COMMON /DKS/ Z,IY
c     STILL 10 STORAGE UNITS, BUT A DIFFERENT SPLIT      
      WRITE(6,900) Z
 900  FORMAT(10H VALUES = , 4F20.5)

      RETURN
      END
      
      SUBROUTINE MYSUB2()
      INTEGER IY(6),I
      REAL Z(4)
      COMMON /DKS/ Z, IY
      DO 10 I=1,6
        IY(I) = I
 10   CONTINUE
      WRITE(6,901) IY
 901  FORMAT(10H VALUES = , 6I10) 
      RETURN
      END
      
      BLOCK DATA
C     CAN INITIALISE LABELLED COMMON      
      REAL X(5),Y(5)
      COMMON /DKS/ X,Y
      DATA X/1.5,2.5,3.5,4.5,5.5/
      DATA Y/10.0,20.0,30.0,40.0,50.0/
      END

ここで留意すべき点は、共通ブロックに名前 DKS が与えられ、一つのプログラム単位で複数の共通ブロックを記述可能なことです。同じ名前が与えられている場合には、共通ブロックが拡張するように定義されます。名前付き共通ブロックは、共有ブロック単位でも初期化することができます。

共有ブロックが普及していた頃は、メモリが不足しがちで大きなプログラムをメモリに保持することができなかったため、プログラムの一部はメモリ外に退避されてオーバーレイによる実行が行われていました。これらにより、ルーチン呼び出しの間で共通ブロック情報を保持する際の規則が決定されました。初期のプログラムは、標準化が受け入れられる前に書かれている可能性があるため、いくつかのバリエーションが存在する可能性があります。一般に、暗黙の SAVE を保証するには、BLOCK DATA を使用した初期化で十分でしたが、次のようなコマンドで明示的に指定できます。

SAVE /DKS/

この例では、共通ブロックはメインプログラムに現れて実行中は保持されますが、メインプログラムには存在せずにルーチン mysub1 と mysub2 にのみ現れる場合は、明示的な SAVE がないと mysub1 と mysub2 の呼び出し間で情報は保存されません。実際にはコンパイラはこれを行うかもしれませんが、そうするように文法上制約はされていません。この事は念頭に置くべきです。

我々は単純に、現代的な Frotran はモジュールというより一般的で安全な共通ブロックの代替物を使うことができると考えますが、ではモジュール内の値の保持のための規則が何であるかと疑問に思うことがあるかもしれません。もともと、これらは共通ブロックの規則を反映したものですが、2005 年以来 ISO / IEC Fortran 標準のプロジェクトエディタを務める nAG のマルコム・コーエンは(nAG Fortran コンパイラの主要開発者であり、nAG メンバーの John Reid と Michael Metcalf と共に Fortran 言語の著作があります)以下の様に答えました:

「Fortran 2008 では、このような状況を許すのは無意味だと誰もが同意し、モジュール変数のデフォルトを SAVE にしました。アロケータブルな変数はユーザーが明示的に割り当てを解除しない限り割り当てられたままです。」

この知識と Fortran 2008 準拠のコンパイラを使用して、上記の古いプログラムを次のように変換します:

    Module myvars
      Real, Allocatable :: x(:)
      Integer, Allocatable :: iy(:)
    End Module

    Module myrouts
    Contains
      Subroutine mysub1()
        Use myvars, Only: x

        Allocate (x(5))
        x = [ 1.5, 2.5, 3.5, 4.5, 5.5 ] ! can assign values
        Print *, x
        x = x(1:4)                 ! can shrink array size
        Return
      End Subroutine

      Subroutine mysub2()
        Use myvars, Only: x, iy
        Integer :: i

        Allocate (iy(6))
        Do i = 1, 6
          iy(i) = i
        End Do
        Print *, 'x-values = ', x
        Print *, 'iy-Values = ', iy
        Deallocate (iy)            ! Can free up memory as soon as it has been
                                   ! used
        Return
      End Subroutine
    End Module

    Program newdksprog
      Use myrouts, Only: mysub1, mysub2
      ! Note module myvars not mentioned.
      Implicit None

      Call mysub1()
      Call mysub2()
      Stop 'ok'
    End Program

モジュール myvars はメインプログラムには現れませんが、問題ありません。何故なら、2008 準拠の nAG コンパイラを使用していることから、mysub1 と mysub2 の呼び出し間にモジュール変数は保存されるためです。

もちろん、実際のプログラム開発はこの記事で示した理想的な観点からは大きく異なります。実際には、開発者は長年に渡って元のプログラムを変更、追加しています。通常は、新しい Fortran 機能が導入された後、プログラマーが役に立つと思う機能を使用できるようにコンパイラーが進化していきます。Fortran 標準委員会は、既存の機能を削除することによって既存のプログラムを失敗させないようにするため、困難な問題が生じる可能性がある中で注意深く進めてられてきました。

EQUIVALENCE 文がこれを示しています。

EQUIVALENCE

この機能は、プログラム単位内の2つ以上の変数または配列が同じメモリ領域を共有することを指定します。その使用はコンパイラの最適化を阻害するため、nAG のコード内での使用は常に最小限に抑えられていましたが、例えば異なる型の配列間でメモリーを共有するといった効果的な使い方で省メモリー化に用いられています。Fortran 言語のアロケータブル配列を用いる際には、多くの場合これは不要です。もちろん Fortran の型も充実してきました。現在の標準では、新しい型と本来の基本型との間で EQUIVALENCE を使用することはできません。

関連情報
MENU
© 日本ニューメリカルアルゴリズムズグループ株式会社 2024
Privacy Policy  /  Trademarks