古典 FORTRAN でのモジュール化、例外処理について

# FORTRAN77 のマイナー命令
Yahoo 知恵袋の質問で FORTRAN77 の ENTRY 文で困っている人がいたようなので、ENTRY 文の使い方を見てみます。

ついでに、文法書ではよくENTRY 文と対になって出てくるけど、使い方は全然関係ない ALTERNATE RETURN 文についても見てみます。

**ENTRY 文は MODULE の祖型、ALTERNATE RETURN は例外処理の祖型**と見なせますが、登場が早すぎて（規格に入ったのは FORTRAN77 からですが、ベンダー拡張として 1960年代中葉 FORTRAN IV/66 の頃までには、一部処理系には導入されていました）機能は限定的で、使い方が難しく、ユーザー側も使い方の本質を理解できずに終わった機能だと思います。

Modern Fortran 規格ではいずれも廃止になったか、廃止勧告が出ています。

![img_0.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44522/80e35233-4ca9-9031-3fd2-a9bdb4b539b6.gif)


## 文法的比較
 
ENTRY 文は SUBROUTINE または FUNCTION の内部に記述出来て、SUBROUTINE/FUNCTION を呼び出すとき、先頭からではなくENTRY 文のある途中から実行できる機能です。

ALTERNATE RETURN は  SUBROUTINE または FUNCTION から RETURN で戻る時に、呼び出し場所ではなく、指定の行番号の場所に戻れるという機能です。

文法書をみると、行きと帰りの分岐で対称的になっているような気がするせいか、並んで出てきたりします。しかし、現在の視点から眺めれば機能的な意味・目的は全く異なるものだと思います。

以下で実例を見てゆきます。

# ENTRY の MODULE 的用法
ここでは SUBROUTINE MODULE の中に、ZERO,LIN,APPLY,PRNT の４つの ENTRY を設けて、これらを Fortran90 以降の Module procedure のように使います。親名前の SUBROUTINE MODULE は呼び出しません。宣言した変数は、各 ENTRY で共有された PRIVATE 属性の MODULE 変数相当のものになります。PRIVATE になるのは、外から直接アクセスできないためです。

### ソース・プログラム
シンタックス・カラリングで、第一カラムの C がコメント行と認識されないので見にくいですが、構造としては Fortran90 の Module とよく似ています。

ENTRY の入れ物の副プログラムを SUBROUTINE で宣言すると、ENTRY になるのは SUBROUTINE だけ、FUNCTION の場合は FUNCTION だけしか許されません。また各 ENTRY 毎のローカル変数宣言も許されません。 

```FORTRAN
C
C     ENTRY AS MODULE PROCEDURE
C
      SUBROUTINE MODULE()
C MODULE VARIABLES : NMAX, X PRIVATE
      PARAMETER (NMAX = 17)
      REAL X(NMAX)
      SAVE X
      EXTERNAL FUN
C CONTAINS
      ENTRY ZERO()
        DO 10 I = 1, NMAX
          X(i) = 0.0
   10   CONTINUE        
        RETURN
C     END ZERO
      
      ENTRY LIN()
        DO 11 I = 1, NMAX
          X(i) = REAL(I)
   11   CONTINUE        
        RETURN
C     END LIN
        
      ENTRY APPLY(FUN)
        DO 12 I = 1, NMAX
          X(I) = FUN(X(I))
   12   CONTINUE      
        RETURN
C     END APPLY
      
      ENTRY PRNT()
        M = 5
        DO 13 I = 1, NMAX, M
          I1 = MIN(I + 4, NMAX)
          PRINT *, (X(J), J = I, I1) 
   13   CONTINUE      
        RETURN
C     END PRNT         
      END 
C     END MODULE 
C
C FUNCTIONS
C
      FUNCTION SQR(X)
        SQR = X * X
        RETURN
      END  
C
      FUNCTION SQROOT(X)
        SQROOT = SQRT(X)
        RETURN
      END  
C
C MAIN PROGRAM
C
      PROGRAM MAIN
      PARAMETER (NMAX = 17)
      EXTERNAL SQR, SQROOT 
      CALL LIN()
      CALL APPLY(SQROOT)
      CALL PRNT()
      PRINT *
      CALL APPLY(SQR)
      CALL PRNT()
      CALL ZERO()
      END
```

### 実行結果
ここでは、関数を引数とするサブルーチンによって、MODULE 変数 X に対する ELEMENTAL 処理を行っています。１から１７までの数のルートをとりまた二乗して元に戻しています。

```
  1.000000       1.414214       1.732051       2.000000       2.236068
  2.449490       2.645751       2.828427       3.000000       3.162278
  3.316625       3.464102       3.605551       3.741657       3.872983
  4.000000       4.123106

  1.000000       2.000000       3.000000       4.000000       5.000000
  6.000000       7.000000       8.000000       9.000000       10.00000
  11.00000       12.00000       13.00000       14.00000       15.00000
  16.00000       17.00000
```

# ALTERNATE RETURN の例外処理的用法
ここでは I/O 処理を例として、例外処理を見てみることにします。ALTERNATE RETURN の前に、まずは READ/WRITE 文組み込みの例外処理の祖型を見てみます。

## READ/WRITE END=xx/ERR=xx
大き目のバッファ配列を用意しておき、サイズの不明なデータを読み込みます。ファイルの最後まで来て EOF (End Of File) が出たら、行番号 99 へ例外処理的に脱出します。なお EOF 以外の error が発生した場合は、行番号 999 へ飛んで、IOSTAT のエラー番号を書き出して実行終了します。  

なお FORTRAN の慣例では 99, 999, 9999 などの行番号は、例外処理的な場合に使われることが多いです。  

### ソース・プログラム
```fortran
C
C   PSEUDO EXCEPTION I/O      
C
      PROGRAM MAIN
        PARAMETER (NMAX = 10, MAXBUF = 9999)
        REAL X(NMAX), BUFF(MAXBUF)
        DO 10 I = 1, NMAX
          X(I) = I  
   10   CONTINUE         
        WRITE(9, *) X
        CLOSE(9)
C       
C TRY        
        READ(9, *, END=99, ERR=999, IOSTAT=IO) (BUFF(I), I = 1, MAXBUF)
C EXCEPT EOF
   99  CONTINUE
          CLOSE(9)
          PRINT *, 'EOF AT RECORD  =', I
          NX = I - 1
          PRINT *, 'NUMBER OF DATA =', NX 
          GOTO 20
C EXCEPT ERROR          
  999   CONTINUE
          CLOSE(9)
          PRINT *, 'I/O ERROR: READING FILE 9'
          PRINT *, 'IOSTAT =', IO
          STOP  
   20   CONTINUE    
C END TRY    
        PRINT *, (X(I), I = 1, NX)  
      END
```

### 実行例
```
 EOF AT RECORD  =          11
 NUMBER OF DATA =          10
   1.000000       2.000000       3.000000       4.000000       5.000000
   6.000000       7.000000       8.000000       9.000000       10.00000
```

## ALTERNATE RETURN
次に、alternate return での例外処理をみてみます。上の例で I/O をサブルーチンの中で行っている状況で、メインルーチン側でエラー処理などをしたい場合、alternate return の出番です。引数として、例外処理用の行番号を渡してやれば、サブルーチン内での RETURN n で渡した行番号へ直接戻ることが出来ます。

### ソース・プログラム
```fortran
C
C   PSEUDO EXCEPTION
C     ALTERNATE RETURN
C      
      SUBROUTINE IOSUB(BUF, NX, *, *)
      REAL BUF(*)
      READ(9, *, END=99, ERR=999, IOSTAT=IO) (BUF(I), I = 1, 9999)   
C EOF
   99 NX = I - 1
      RETURN 1
C ERROR      
  999 CONTINUE
      PRINT *, 'I/O ERROR IOSTAT =', IO 
      RETURN 2
      END
C
C
C      
      PROGRAM MAIN
      PARAMETER (MX=10, NMAX = 9999)
      REAL X(MX), BUF(NMAX)
C
      DO 10 I = 1, MX
        X(I) = I
   10 CONTINUE     
      WRITE(9, *) X
      CLOSE(9)
C
      CALL IOSUB(BUF, NX, *99, *999)
      STOP
C      
C EXCEPTION END OF FILE
   99 PRINT *, 'NUMBER OF DATA =', NX
      PRINT *, (X(I), I = 1, NX)  
      STOP 'NORMAL END'
C
C EXCEPTION I/O ERROR      
  999 STOP 'ABNORMAL END'
      END 
```

### 実行例
```
 NUMBER OF DATA =          10
   1.000000       2.000000       3.000000       4.000000       5.000000
   6.000000       7.000000       8.000000       9.000000       10.00000
NORMAL END
```

# まとめ
以上の実例から、FORTRAN77 以前から FORTRAN では、モジュラー型のプログラミングや、例外処理による例外的な状況を、通常処理から隔離して記述するような試みを行っていたことを見てきました。

しかしながら、使ってみればわかりますが、色々と制約が多くて使いづらく、文法的にも理解しにくい所があります。

結局、Fortran90 で MODULE 構文が導入されるまでは、ENTRY 文で変数共有するよりは COMMON 文を使って変数を共有させた方がよいとされていましたし、多分それが事実でしょう。

また ALTERNATE RETURN ですが、本質的に大域 GOTO 文であって、かつもしエラー番号を運ぶとするなら、何らかの手段で大域変数を持たなければならないので、局所解決を目指した構造化プログラミングとは相容れない構文であることが分かります。

このため alternate return はその後の規格からの廃止項目となり、例外処理は Modern Fortran には採用されてこなかったものと思われます。