本記事は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 を使用することはできません。