愛しの Fortran・3改

Fortran について気の向くままに綴ります

実数値を文字列に変換する関数 (配列編)

前回までで、書式仕様の指定を簡略化する目的で、 実数値を文字列に変換する関数を実装しました。

かつては WRITE 文で呼び出された関数で WRITE 文を実行することは禁じられていました(再帰的 WRITE の禁止)。 Fortran 2003 で WRITE 文で呼び出された関数内での 「単純な」内部ファイルの WRITE は許可されたことも 前回の関数を実装できた背景にありました (と、前回説明するのを忘れた)。

今回はさらに配列引数への拡張です。

単に ELEMENTAL にすれば良さそうなものですが…。

文字配列の配列要素は全て長さが共通です。 だから組込み関数 TRIM は要素別処理関数ではありません。 前回のやりかたでは ELEMENTAL にはできないことがわかります (前々回の結果の長さを引数で与える方式ならできそう)。

今回は 1 次元配列の実装を別に用意することにします。 下がそのプログラムです。 どうしてこうなるのか。 各部で説明することが多いので、それは次回とします。

(つづく)

MODULE MODULE

      USE, INTRINSIC :: IEEE_ARITHMETIC

      INTEGER, PARAMETER :: &
       R_ = IEEE_SELECTED_REAL_KIND(13)

      INTEGER, PARAMETER :: L_ = 132

CONTAINS

!
      FUNCTION F_(A,FMT) RESULT(RESULT)
      REAL(R_), INTENT(IN) :: A(:)
      CHARACTER(*), INTENT(IN) :: FMT
      CHARACTER(:), ALLOCATABLE :: RESULT(:)

      INTEGER :: I, L, N
      CHARACTER(L_) :: LINE

      N = UBOUND(A,1)

      L = 0
      DO I = 1, N
        WRITE(LINE,FMT) A(I)
        L = MAX(LEN_TRIM(LINE),L)
      END DO

      ALLOCATE(CHARACTER(L) :: RESULT(N))

      DO I = 1, N
        WRITE(LINE,FMT) A(I)
        RESULT(I) = LINE
      END DO
      END FUNCTION F_

END MODULE MODULE

      PROGRAM PROGRAM
      USE :: MODULE

      WRITE(*,"(*(G0,1X))") &
       F_([ 12.3_R_, -9.87_R_, 0.0_R_ ],"(F0.2)")
      END PROGRAM PROGRAM

実数値を文字列に変換する関数 (その2)

さて、前回の関数 F_(A,N,FMT) は実数値 A と書式仕様 FMT の他に 関数結果の長さ N を引数としていました。 どうして書式仕様を与えているのに、さらに N が必要だったのか。 Fortran 95 までは関数結果の文字変数の長さは関数の開始時に確定していることが必要でした。 その当時の書き方だったからです。

ところで、結果変数に直接 WRITE せず、いったん別に用意した LINE に書いたのを コピーしてました。

      CHARACTER(MAX(L_,N)) :: LINE

      WRITE(LINE,FMT) A
      RESULT = LINE

これはユーザーが指定する結果変数が短かすぎることによるバッファオーバーフローを 避けるためです。 L_ は十分な長さと仮定しています。

なお、前回の出力結果はわざと末尾に空白が 1 つ付く 6 文字にしてありましたが、 ブログでは末尾の空白が消えて 5 文字になってしまいました。 markdownはてなブログmarkdown の仕様でしょうか。

さて、ここからが今日の本題。 Fortran 2003 以降は文字型の結果変数を ALLOCATABLE にできます。

      CHARACTER(:), ALLOCATABLE :: RESULT

結果変数の割り付けと代入は 1 文でできます。

      RESULT = TRIM(LINE)

代入時に RESULT が割り付けられていないか、TRIM(LINE) とは長さが違う場合、 右辺の長さに割り付けられて代入されます。 前回よりかは改善されたかと思います。 下に改訂版のプログラムを付けます。

ところで、実数の配列の値も出力したいですよね? これ意外に簡単じゃないです。

(つづく)

MODULE MODULE

      USE, INTRINSIC :: IEEE_ARITHMETIC

      INTEGER, PARAMETER :: &
       R_ = IEEE_SELECTED_REAL_KIND(13)

      INTEGER, PARAMETER :: L_ = 132

CONTAINS

!
      FUNCTION F_(A,FMT) RESULT(RESULT)
      REAL(R_), INTENT(IN) :: A
      CHARACTER(*), INTENT(IN) :: FMT
      CHARACTER(:), ALLOCATABLE :: RESULT

      CHARACTER(L_) :: LINE

      WRITE(LINE,FMT) A
      RESULT = TRIM(LINE)
      END FUNCTION F_

END MODULE MODULE

      PROGRAM PROGRAM
      USE :: MODULE

      WRITE(*,"(*(G0))") '"', F_(12.3_R_,"(F0.2)"), '"'
      END PROGRAM PROGRAM

実数値を文字列に変換する関数

初回なのに、これまでのあらすじから。

Fortran 2008 で編集記述子 G0 が導入されて普段使いの書式仕様は (*(G0,1X))(*(G0)) で済むことに。しかし実数値の満足な出力には有効桁数の指定が必要。ならば書式仕様の方はそのままで、実数値は文字列に変換して出力項目にすればいいのでは…

(詳しくは 最新 Fortran 書式仕様 参照。)

そこで今回はとりあえず書いてみましょう。

MODULE MODULE

      USE, INTRINSIC :: IEEE_ARITHMETIC

      INTEGER, PARAMETER :: &
       R_ = IEEE_SELECTED_REAL_KIND(13)

      INTEGER, PARAMETER :: L_ = 132

CONTAINS

!
      FUNCTION F_(A,N,FMT) RESULT(RESULT)
      INTEGER, INTENT(IN) :: N
      REAL(R_), INTENT(IN) :: A
      CHARACTER(*), INTENT(IN) :: FMT
      CHARACTER(N) :: RESULT

      CHARACTER(MAX(L_,N)) :: LINE

      WRITE(LINE,FMT) A
      RESULT = LINE
      END FUNCTION F_

END MODULE MODULE

      PROGRAM PROGRAM
      USE :: MODULE

      WRITE(*,"(*(G0))") &
       '"', F_(12.3_R_,6,"(F0.2)"), '"'
      END PROGRAM PROGRAM

出力結果は「12.30」。 意図は実現できそうですが…

(つづく)