で、ロジックを推測して論理的に書きなおすのであるが、プログラムを壊してしまっていないことを確認しながらバージョン管理をすすめていかないと険呑このうえない。 そこでアサーション(想定していない動作をしたら落ちるような確認)をほうぼうに入れていくのだが、なにせFORTRANだからその対象はきまって巨大な配列である。1行で確認するためには、定数データをプログラムに埋め込むわけにいかないから、外部にファイルで持つべきである。
で、こんなサブルーチンを書いてみた。
まず、プログラムの要所要所で、壊れては困るデータを第3引数VALにしてASSERTを呼ぶ。文字型でなければ何でもよい。第2引数SIZは要素数(INTEGER, REAL, LOGICALの場合)あるいはその2倍(DOUBLE PRECISION, COMPLEXの場合)である。配列でなくても1要素配列とみなせばよい。第1引数には16文字以下で行番号なり適当な名前を入れて置く。
で、まずいじっていないプログラムを実行すると ASSERTDAT というファイルが生じるので、これを大事に保存して、次にプログラムをいじって実行すると、動作が変化したときには落ちるというわけである。
アサーションの場所を増減したいこともあるかもしれない。減らすのは簡単で、CALL ASSERT を削除すれば、名前がマッチしない場合は ASSERTDAT 内のレコードを読み飛ばす。増やすのは、しかたがないので、最初からやりなおしである。
SUBROUTINE ASSERT(NAM, SIZ, VAL)
IMPLICIT NONE
CHARACTER(*), INTENT(IN):: NAM
INTEGER, INTENT(IN):: SIZ
INTEGER, INTENT(IN):: VAL(*)
CHARACTER(16):: INAM, FNAM
INTEGER, SAVE:: UNI = -1
LOGICAL, SAVE:: RMODE = .TRUE.
LOGICAL:: OP
INTEGER:: FSIZ, IOS, I
INTEGER, ALLOCATABLE:: FVAL(:)
CHARACTER(*), PARAMETER:: DFN = 'ASSERTDAT'
LOGICAL, SAVE:: DBG = .FALSE.
IF (UNI < 0) THEN
DO, UNI = 40, 100
INQUIRE(UNIT=UNI, OPENED=OP)
IF (.NOT. OP) EXIT
ENDDO
OPEN(UNI, FILE=DFN, STATUS='OLD', ACTION='READ',
$ POSITION='REWIND',
$ FORM='UNFORMATTED', ACCESS='SEQUENTIAL', IOSTAT=IOS)
IF (IOS == 0) THEN
CONTINUE
IF (DBG) WRITE(0, '(A,I3)') '!OPEN-READ', UNI
ELSE
IF (DBG) WRITE(0, '(A,I3)') '!OPEN-WRITE', UNI
RMODE = .FALSE.
OPEN(UNI, FILE=DFN, STATUS='NEW', ACTION='WRITE',
$ POSITION='REWIND',
$ FORM='UNFORMATTED', ACCESS='SEQUENTIAL')
ENDIF
ENDIF
INAM = NAM
IF (DBG) WRITE(0, '(A,I)') '!TRY '// INAM, SIZ
IF (RMODE) THEN
DO
READ(UNI) FNAM, FSIZ
IF (FNAM == INAM) THEN
IF (FSIZ /= SIZ) THEN
WRITE(0, *) 'ASSERT: SIZE MISMATCH', INAM, SIZ, FSIZ
STOP 16
ENDIF
ALLOCATE(FVAL(SIZ))
READ(UNI) FVAL(1:SIZ)
IF (.NOT.ALL(FVAL(1:SIZ)==VAL(1:SIZ))) THEN
DO, I = 1, SIZ
IF (FVAL(I) /= VAL(I)) THEN
WRITE(0, '(A,A,":",3Z9.8)')
$ '!ASSERT:', INAM, I, FVAL(I), VAL(I)
ENDIF
ENDDO
STOP 16
ENDIF
DEALLOCATE(FVAL)
EXIT
ELSE
IF (DBG) WRITE(0, '(A,A)') '!PASS', FNAM
READ(UNI)
ENDIF
ENDDO
ELSE
WRITE(UNI) INAM, SIZ
WRITE(UNI) VAL(1:SIZ)
ENDIF
END SUBROUTINE
0 件のコメント:
コメントを投稿