fortran66のブログ

fortran について書きます。

FORTRAN77で再帰を使ってQUICK SORT

たまたま面白いサイトに出くわしました。本来再帰が許されていない FORTRAN77 で、シンタックスエラーを出さず、違法と合法のスレスレで再帰を実現する方法を発見した方がおられました。
http://www.esm.psu.edu/~ajm138/fortranexamples.html

自分自身を関数引数としてとるという、今まで聞いたことも見たこともない*1非常に興味深い方法で再帰を実現していて大変関心させられました。FORTRAN77 は規格の上では、副プログラムの変数は AUTOMATIC になっているので、引数の類をスタックに積むような事は自動でやってくれています。したがって、プログラムに何ら無理で不自然なところはありません。

ただ処理系によっては FORTRAN66 との互換性を保つために、デフォルトで副プログラムの変数を SAVE 扱いにしている場合もままあり、その場合はコンパイラオプションで本来の規格に忠実に AUTOMATIC 変数扱いにする必要があります。

非常に面白いアイデアで、私的には近年まれにみるヒット作で、FORTRAN77 もまだまだ奥深いなと思いました。

ついでなので、このアイデアに基づいて QuickSort を書いてみました。

実行結果

100個の実数を整列させています。DEBUG MODE ではサイズ0の配列を渡す時に配列はみだしエラーが起きますが、特に問題はありません。

ソース・プログラム

RANDOM_NUMBER() は Fortran90 のサブルーチンですが、面倒なので利用しました。それ以外はFORTRAN77 の範囲で記述したつもりです。(print *, x も 90 かw)

      PROGRAM MAIN
      PARAMETER(NMAX = 10**7)
      REAL X(NMAX), WK(NMAX)
      EXTERNAL QSORT
C  Fortran90 : random_number     
      call random_number(X) 
      N = NMAX
      CALL QSORT(N, X, WK, QSORT)
      print *, any(x(1:n - 1) > x(2:))
!      print *, x
      STOP
      END

C  FORTRAN77 RECURSIVE SUBROUTINE
C  based on the idea by Andrew J. Miller http://www.esm.psu.edu/~ajm138/fortranexamples.html
      SUBROUTINE QSORT(N, X, WK, DUMSUB)
      REAL X(N), WK(N)
      EXTERNAL DUMSUB
      IF (N .LE. 1) RETURN
      K = 1
      J = N
      PIVOT = X(1)
      DO 10 I = 2, N
        IF (X(I) .LT. PIVOT) THEN
          WK(K) = X(I)
          K = K + 1
        ELSE
          WK(J) = X(I)
          J = J - 1
        END IF
10    CONTINUE
      DO 20 I = 1, K - 1
        X(I) = WK(I)
20    CONTINUE
      X(K) = PIVOT
      DO 30 I = K + 1, N 
        X(I) = WK(I)
30    CONTINUE
      CALL DUMSUB(K - 1, X,        WK,        DUMSUB)
      CALL DUMSUB(N - K, X(K + 1), WK(K + 1), DUMSUB)                
      RETURN
      END

*1:でもどっかで見たような気がしたので、岩波FORTRAN辞典を見返してみたら、階乗の再帰的呼び出し例で、全く同じアイデアのプログラム例が載ってました。P.331