コンテンツにスキップ

Fortran/Fortranの活用例

出典: フリー教科書『ウィキブックス(Wikibooks)』

以下のFortranのコード例やサンプルプログラムは、コンパイラに依存するさまざまな状況を示しています。最初の一連の例は、Fortran II、IV、および77のコンパイラ用です。残りの例は、新しい標準のFortranコンパイラでコンパイルおよび実行できます(コンパイラのリストについてはFortran記事の末尾を参照してください)。通常、ほとんどの現代のFortranコンパイラは、ソースコードファイル名の接尾辞に基づいてコンパイル中に使用する言語標準を選択します:.f(またはまれに.for)に対してFORTRAN 77、.f90に対してFortran 90、.f95に対してFortran 95。他の標準がサポートされている場合、コマンドラインオプションで手動で選択できます。

FORTRAN II、IV、および77のコンパイラ

[編集]

注:FORTRAN 90以前では、ほとんどのFORTRANコンパイラが固定フォーマットのソースコードを強制しました。これはIBMパンチカードの遺物です。 コメントは列1に*またはCまたは!で始まる必要があります。 ステートメントラベルは列1から列5に配置する必要があります。 継続行には列6に非空白文字が必要です。 ステートメントは列7から始まる必要があります。 行の長さは72文字に制限される場合があります(パンチカードの80バイト幅から派生し、最後の8文字は(任意の)シーケンス番号用に予約されています)。 FORTRANコードをコンパイルする際にエラーが発生した場合は、まず列の整列を確認してください。 一部のコンパイラは、コンパイラフラグを使用してフリーフォームソースを提供します

三角形の面積プログラム

[編集]

単純なFortran IIプログラム

[編集]
1枚のデータカード入力

入力値の1つがゼロの場合、プログラムはプログラムの実行後のジョブコントロールカードリストでエラーコード「1」で終了します。通常、A、B、C、およびAREAが1行で印刷されます。特定の単位は指定されていません。

C 三角形の面積 - ヘロンの公式
C 入力 - カードリーダーユニット5、整数入力
C 出力 -
C INTEGER VARIABLES START WITH I,J,K,L,M OR N
      READ(5,501) IA,IB,IC
  501 FORMAT(3I5)
      IF (IA) 701, 777, 701
  701 IF (IB) 702, 777, 702
  702 IF (IC) 703, 777, 703
  777 STOP 1
  703 S = (IA + IB + IC) / 2.0
      AREA = SQRT( S * (S - IA) * (S - IB) * (S - IC) )
      WRITE(6,801) IA,IB,IC,AREA
  801 FORMAT(4H A= ,I5,5H  B= ,I5,5H  C= ,I5,8H  AREA= ,F10.2,
     $13H SQUARE UNITS)
      STOP
      END

シンプルなFortran IVプログラム

[編集]
複数のデータカード入力

このプログラムには2つの入力チェックがあります。1つはデータの終わりを示す空白のカード用であり、もう1つは入力データ内のゼロ値用です。どちらの条件でも、メッセージが表示されます。

C 三角形の面積 - ヘロンの公式
C 入力 - カードリーダーユニット5、整数入力、データの終わりのための1つの空白のカード
C 出力 - ラインプリンターユニット6、実数出力
C 入力エラーは出力にエラーメッセージを表示します
  501 FORMAT(3I5)
  601 FORMAT(4H A= ,I5,5H  B= ,I5,5H  C= ,I5,8H  AREA= ,F10.2,
     $13H SQUARE UNITS)
  602 FORMAT(10HNORMAL END)
  603 FORMAT(23HINPUT ERROR, ZERO VALUE)
      INTEGER A,B,C
   10 READ(5,501) A,B,C
      IF(A.EQ.0 .AND. B.EQ.0 .AND. C.EQ.0) GO TO 50
      IF(A.EQ.0 .OR.  B.EQ.0 .OR.  C.EQ.0) GO TO 90
      S = (A + B + C) / 2.0
      AREA = SQRT( S * (S - A) * (S - B) * (S - C) )
      WRITE(6,601) A,B,C,AREA
      GO TO 10
   50 WRITE(6,602)
      STOP
   90 WRITE(6,603)
      STOP
      END

シンプルなFortran 77プログラム

[編集]
複数のデータカード入力

このプログラムには2つの入力チェックがあります。1つはデータの終わりを示す空白のカード用であり、もう1つはゼロ値と有効なデータが入力された場合のチェックです。どちらの条件でも、メッセージが表示されます。

C 三角形の面積 - ヘロンの公式
C 入力 - カードリーダーユニット5、整数入力、データの終わりのための空白のカードはありません
C 出力 - ラインプリンターユニット6、実数出力
C 入力エラーは出力にエラーメッセージを表示します
  501 FORMAT(3I5)
  601 FORMAT(" A= ",I5,"  B= ",I5,"  C= ",I5,"  AREA= ",F10.2,
     $"SQUARE UNITS")
  602 FORMAT("NORMAL END")
  603 FORMAT("INPUT ERROR OR ZERO VALUE ERROR")
      INTEGER A,B,C
   10 READ(5,501,END=50,ERR=90) A,B,C
      IF(A=0 .OR. B=0 .OR. C=0) GO TO 90
      S = (A + B + C) / 2.0
      AREA = SQRT( S * (S - A) * (S - B) * (S - C) )  
      WRITE(6,601) A,B,C,AREA
      GO TO 10
   50 WRITE(6,602)
      STOP
   90 WRITE(6,603)
      STOP
      END

「レトロ」FORTRAN IV

[編集]

FORTRAN IVのレトロなプログラムデッキの例が、IBM 1130ページで入手可能です。コンパイルと実行に必要なIBM 1130 DM2 JCLも含まれています。IBM 1130エミュレータはIBM 1130.orgで利用可能であり、FORTRAN IVプログラムをPC上でコンパイルおよび実行することができます。

Hello, Worldプログラム

[編集]

コンピューティングの伝統に従って、最初に紹介される例は、画面(またはプリンター)に「Hello, world」と表示する単純なプログラムです。

FORTRAN 66(またFORTRAN IV)

[編集]
 C     FORTRAN IVは最初のプログラミング言語の一つで
 C     ソースコメントをサポートしていました
       WRITE (6,7)
     7 FORMAT(13H HELLO, WORLD)
       STOP
       END

このプログラムは、「HELLO, WORLD」をFortranユニット番号6に出力します。ほとんどのマシンでは、これはラインプリンターまたは端末でした(パンチカードリーダーやキーボードは通常、ユニット5として接続されていました)。WRITE文中の数字7は、対応するFORMAT文の文番号を指します。FORMAT文は、それを参照するWRITE文と同じプログラムや関数/サブルーチンブロック内のどこにでも配置できます。通常、FORMAT文は、それを呼び出すWRITE文の直後に配置されますが、代替として、FORMAT文はプログラムやサブプログラムブロックの最後にまとめて配置されることもあります。実行がFORMAT文に流れ込んだ場合、それはノーオペレーションとなります。したがって、上記の例ではWRITESTOPの2つの実行可能な文しかありません。

上記の例のFORMAT文内の最初の13Hは、ホーラス定数を定義しており、ここでは直後の13文字を文字定数として取り込むことを意味しています(ホーラス定数は区切り文字で囲まれていません)。(一部のコンパイラは、シングルクォーテーションで囲まれた文字リテラルをサポートしており、これはFORTRAN 77で標準となりました。)

13Hの直後のスペースは、出力時に新しい行に進むようにI/Oシステムに指示するキャリッジ制御文字です。この位置にゼロがあると、2行進みます(ダブルスペース)、1が新しいページの先頭に進みます。また、+文字は新しい行に進まず、上書きを許可します。

FORTRAN 77

[編集]

FORTRAN 77では、文字リテラルを区切るためにシングルクォーテーションが使用され、FORMAT文への参照の代わりにインラインの文字列が使用されるようになりました。コメント行は、列1にCまたはアスタリスク(*)を使用して示すことができます。

      PROGRAM HELLO
*     PRINT文はWRITEと似ていますが、
*     標準出力ユニットに出力します
        PRINT '(A)', 'Hello, world'
        STOP
      END

Fortran 90

[編集]

Fortran 90では、シングルクォーテーションに加えてダブルクォーテーションが許可されます。FORTRAN 77でサポートされているように、list-directed I/Oを使用したHello, worldの更新されたバージョンは、次のようにFortran 90で書くことができます:

 program HelloWorld
   write (*,*) 'Hello, world!'   ! これが一行コメント
 end program HelloWorld

Fortran 77の例

[編集]

最大公約数

[編集]

以下のFORTRAN 77の入門的な例は、最大公約数の2つの数に対して、ユークリッドのアルゴリズムを用いて求めます。

 
*     euclid.f (FORTRAN 77) 
*     ユークリッドのアルゴリズムを使用して最大公約数を見つける
      PROGRAM EUCLID
        PRINT *, 'A?'
        READ *, NA
        IF (NA.LE.0) THEN
          PRINT *, 'A must be a positive integer.'
          STOP
        END IF
        PRINT *, 'B?'
        READ *, NB
        IF (NB.LE.0) THEN
          PRINT *, 'B must be a positive integer.'
          STOP
        END IF
        PRINT *, 'The GCD of', NA, ' and', NB, ' is', NGCD(NA, NB), '.'
        STOP
      END

      FUNCTION NGCD(NA, NB)
        IA = NA
        IB = NB
    1   IF (IB.NE.0) THEN
          ITEMP = IA
          IA = IB
          IB = MOD(ITEMP, IB)
          GOTO 1
        END IF
        NGCD = IA
        RETURN
      END

上記の例は以下を示すために意図されています:

  • 上記のPRINTREAD文では、'*'をフォーマットとして使用しており、リスト指向のフォーマットを指定しています。リスト指向のフォーマットは、次の引数に基づいて必要な入力または出力のフォーマットをコンパイラに推測させるようにします。
  • FORTRANの初期のマシンでは文字セットが制限されていたため、FORTRAN 77では関係演算子 =、≠、<、>、≤、および ≥ を表す.EQ..NE..LT..GT..LE..GE.のような略記が使用されます。
  • この例では、暗黙の型指定機構を使用して、NANBIAIB、および ITEMP のINTEGER型を指定します。
  • 関数NGCD(NA, NB)では、関数引数NANBの値がそれぞれローカル変数IAIBにコピーされます。これは、IAIBの値が関数内で変更されるために必要です。Fortranの関数およびサブルーチンでの引数の受け渡しはデフォルトで参照渡しを利用します(値渡しではなく、例えばCのような言語のデフォルトとは異なります)。関数内からNANBを修正すると、関数を呼び出したメインのPROGRAMユニット内の対応する 実引数 が実質的に修正されることになります。

以下に、プログラムのコンパイルと実行の結果を示します。

$ g77 -o euclid euclid.f
$ euclid
 A?
24
 B?
36
 The GCD of 24 and 36 is 12.

複素数

[編集]

以下のFORTRAN 77の例は、としたときのの値(ここで虚数単位 です)をの値に対して出力します。

*     cmplxd.f (FORTRAN 77)
*     Demonstration of COMPLEX numbers
*
*     Prints the values of e ** (j * i * pi / 4) for i = 0, 1, 2, ..., 7
*         where j is the imaginary number sqrt(-1)

      PROGRAM CMPLXD
        IMPLICIT COMPLEX(X)
        PARAMETER (PI = 3.141592653589793, XJ = (0, 1))
        DO 1, I = 0, 7
          X = EXP(XJ * I * PI / 4)
          IF (AIMAG(X).LT.0) THEN
            PRINT 2, 'e**(j*', I, '*pi/4) = ', REAL(X), ' - j',-AIMAG(X)
          ELSE
            PRINT 2, 'e**(j*', I, '*pi/4) = ', REAL(X), ' + j', AIMAG(X)
          END IF
    2     FORMAT (A, I1, A, F10.7, A, F9.7)
    1     CONTINUE
        STOP
      END

上記の例は以下を示すために意図されています:

  • IMPLICIT文を使用して、変数の暗黙の型を指定することができます。デフォルトの暗黙の型付けスキームと異なる場合、変数の最初の文字に基づいてその暗黙の型を指定します。この例では、この文は変数の暗黙の型をCOMPLEXに指定します。
  • PARAMETER文は定数を指定するために使用できます。この例の2番目の定数(XJ)は、複素数値の値を持ちます。ここで、虚数単位です。
  • DO文内の最初の数値は、DOループ内の最終文とみなされる文の番号を指定します。この例では、END IFまたはFORMATが単一の実行文ではないため、(何もしない) CONTINUE文が単にループの最終文として指定されます。
  • EXP()は指数関数に対応します。FORTRAN 77では、これはジェネリック関数であり、複数の型(REALや、この例ではCOMPLEXなど)の引数を受け入れることを意味します。*FORTRAN 66では、特殊関数を呼び出す必要がありました。これは、関数引数の型に応じて名前が異なります(この例ではCOMPLEX値の場合にCEXP())。
  • COMPLEX値の場合、REAL()およびAIMAG()は、それぞれ引数の実部と虚部の値を返します。

ちなみに、上記のプログラムの出力は以下の通りです(これらの値は、オイラーの公式による単位円周上に均等に配置された8つの点としての幾何学的解釈についての記事を参照してください)。

$ cmplxd
e**(j*0*pi/4) =  1.0000000 + j0.0000000
e**(j*1*pi/4) =  0.7071068 + j0.7071068
e**(j*2*pi/4) =  0.0000000 + j1.0000000
e**(j*3*pi/4) = -0.7071068 + j0.7071068
e**(j*4*pi/4) = -1.0000000 - j0.0000001
e**(j*5*pi/4) = -0.7071066 - j0.7071069
e**(j*6*pi/4) =  0.0000000 - j1.0000000
e**(j*7*pi/4) =  0.7071070 - j0.7071065

上記の数値のいくつかの場合、最後の小数位でエラーが発生していることが見て取れます。これは、COMPLEXデータ型が実部と虚部を単精度で表現しているためです。ちなみに、Fortran 90では倍精度の複素数データ型も標準化されました(しかし、いくつかのコンパイラはそれ以前からこのような型を提供していました)。

三角形の面積を求めるFORTRAN 90プログラム

[編集]
program area
    implicit none
    real :: A, B, C, S

    ! area of a triangle
    read *, A, B, C
    S = (A + B + C)/2
    A = sqrt(S*(S-A)*(S-B)*(S-C))
    print *,"area =",A
    stop
end program area

Fortran 90/95の例

[編集]

DOループを使用した合計値の計算

[編集]

このFortran 90のコードの例では、プログラマーはコードの大部分をDOループの中に書いています。実行時には、指示が画面に表示され、SUM変数がループの外でゼロに初期化されます。ループが開始すると、ユーザーに任意の数値の入力を要求します。この数値は、ループが繰り返されるたびに変数SUMに加算されます。ユーザーが0を入力すると、EXIT文がループを終了し、SUMの値が画面に表示されます。

このプログラムには、データファイルも含まれています。ループが始まる前に、プログラムは(もしあれば) "SumData.DAT"というテキストファイルを作成(または開く)します。ループ中、WRITE文はこのファイルに入力された数値を保存し、ループが終了すると回答も保存します。

! sum.f90 
! EXIT文を使用してループを使用した合計値を計算します 
! 入力情報と合計値をデータファイルに保存します

program summation
    implicit none
    integer :: sum, a

    print *, "This program performs summations. Enter 0 to stop."
    open (unit=10, file="SumData.DAT")
    sum = 0
    do
        print *, "Add:"
        read *, a
        if (a == 0) then
            exit
        else
            sum = sum + a
        end if
        write (10,*) a
    end do

    print *, "Summation =", sum
    write (10,*) "Summation =", sum
    close(10)
end

実行すると、コンソールに次のように表示されます。

 This program performs summations.  Enter 0 to stop.
 Add:
1
 Add:
2
 Add: 
3
 Add:
0
 Summation = 6

そして、ファイルSumData.DATには次のように表示されます。

1
2
3
Summation = 6

円柱の表面積の計算

[編集]

次のプログラムは、円柱の表面積を計算するものであり、自由形式のソース入力やFortran 90で導入された他の機能を示しています。

program cylinder

! 円柱の表面積を計算します。
!
! 変数と定数の宣言
! 定数=pi
! 変数=半径の2乗と高さ

  implicit none    ! すべての変数を明示的に宣言することを要求する

  integer :: ierr
  character(1) :: yn
  real :: radius, height, area
  real, parameter :: pi = 3.141592653589793

  interactive_loop: do

!   ユーザーに半径と高さを入力するように促し、それらを読み取ります。

    write (*,*) 'Enter radius and height.'
    read (*,*,iostat=ierr) radius,height

!   半径と高さが入力から読み取れなかった場合、 
!   ループを通じて進みます。

    if (ierr /= 0) then
      write(*,*) 'Error, invalid input.'
      cycle interactive_loop
    end if

!   面積を計算します。 **は「累乗」を意味します。

    area = 2*pi * (radius**2 + radius*height)

!   入力変数(半径、高さ)と出力(面積)を画面に書き込みます。

    write (*,'(1x,a7,f6.2,5x,a7,f6.2,5x,a5,f6.2)') &
      'radius=',radius,'height=',height,'area=',area

    yn = ' '
    yn_loop: do
      write(*,*) 'Perform another calculation? y[n]'
      read(*,'(a1)') yn
      if (yn=='y' .or. yn=='Y') exit yn_loop
      if (yn=='n' .or. yn=='N' .or. yn==' ') exit interactive_loop
    end do yn_loop

  end do interactive_loop

end program cylinder

動的メモリ割り当てと配列

[編集]

次のプログラムは、動的メモリ割り当てと配列ベースの操作を示しており、Fortran 90で導入された2つの機能を説明しています。配列の操作には、DOループやIF / THEN文がないことが特に注目されます。数学的な操作は配列全体に適用されます。また、記述的な変数名の使用と、現代のプログラミングスタイルに準拠した一般的なコードのフォーマットも明らかです。この例では、インタラクティブに入力されたデータの平均値を計算します。

program average

! いくつかの数値を読み取り、平均値を取得します
! この例では、データポイントがない場合、ゼロの平均値が返されます
! これは望ましくない動作かもしれませんが、この例を単純化します

  implicit none
  integer :: number_of_points
  real, dimension(:), allocatable :: points
  real :: average_points=0., positive_average=0., negative_average=0.

  write (*,*) "Input number of points to average:"
  read (*,*) number_of_points

  allocate (points(number_of_points))

  write (*,*) "Enter the points to average:"
  read (*,*) points

! ポイントを合計し、number_of_pointsで割ることで平均値を取ります
  if (number_of_points > 0) average_points = sum(points)/number_of_points

! 今度は正のポイントと負のポイントだけで平均を取ります
  if (count(points > 0.) > 0) positive_average = sum(points, points > 0.) &
        /count(points > 0.)
  if (count(points < 0.) > 0) negative_average = sum(points, points < 0.) &
        /count(points < 0.)

  deallocate (points)

! 結果を端末に出力します
  write (*,'(''Average = '', 1g12.4)') average_points
  write (*,'(''Average of positive points = '', 1g12.4)') positive_average
  write (*,'(''Average of negative points = '', 1g12.4)') negative_average

end program average

関数の記述

[編集]

以下の例は、線形方程式系を解く関数を示しています。Fortranで利用可能なモダンな機能、例えば、遅延形状、保護された引数、オプション引数などが使用されています。

function gauss_sparse(num_iter, tol, b, A, x, actual_iter) result(tol_max)

! この関数は、ガウス・ザイデル法を使用して方程式系(Ax = b)を解きます

   implicit none

   real ::  tol_max

!  入力:その値は関数内で変更できません
   integer, intent(in) :: num_iter
   real, intent(in) :: tol
   real, intent(in), dimension(:) :: b, A(:,:)

!  入力/出力:その入力値は関数内で使用され、変更できます
   real, intent(inout) :: x(:)

!  出力:その値は関数内で変更されますが、引数が必要な場合のみ
   integer, optional, intent(out) :: actual_iter

!  ローカル
   integer :: i, n, iter
   real :: xk

!  値を初期化します
   n = size(b)  ! 配列のサイズ、size組込み関数を使用して取得します
   tol_max = 2. * tol
   iter = 0

!  収束するまで解を計算します
   convergence_loop: do while (tol_max >= tol .and. iter < num_iter); iter = iter + 1

      tol_max = -1.  ! 許容誤差の値をリセットします

!     k番目の反復の解を計算します
      iteration_loop: do i = 1, n

!        現在のx値を計算します
         xk = (b(i) - dot_product(A(i,:i-1),x(:i-1)) - dot_product(A(i,i+1:n),x(i+1:n))) / A(i, i)

!        解の誤差を計算します
!        dot_product(a,v)=a'b
         tol_max = max((abs(x(i) - xk)/(1. + abs(xk))) ** 2, abs(A(i, i) * (x(i) - xk)), tol_max)
         x(i) = xk
      enddo iteration_loop
   enddo convergence_loop

   if (present(actual_iter)) actual_iter = iter

end function gauss_sparse

このルーチンへの明示的なインターフェースが、その呼び出し元によって利用可能である必要があります。これは、関数をモジュール内に配置し、呼び出しルーチンでそのモジュールをUSEすることによって、最も望ましい方法です。別の方法は、次の例に示すように、INTERFACEブロックを使用することです。

program test_gauss_sparse
    implicit none

!   gauss_sparse関数への明示的なインターフェース
    interface
        function gauss_sparse(num_iter, tol, b, A, x, actual_iter) result(tol_max)
           real ::  tol_max
           integer, intent(in) :: num_iter
           real, intent(in) :: tol
           real, intent(in), dimension(:) :: b, A(:,:)
           real, intent(inout) :: x(:)
           integer, optional, intent(out) :: actual_iter
        end function
    end interface

!   変数の宣言
    integer :: i, N = 3, actual_iter
    real :: residue
    real, allocatable :: A(:,:), x(:), b(:)

!   配列の割り当て
    allocate (A(N, N), b(N), x(N))

!   行列の初期化
    A = reshape([(real(i), i = 1, size(A))], shape(A))

!   対角要素が他の要素の合計になるように行列を変更する
    do i = 1, size(A, 1)
        A(i,i) = sum(A(i,:)) + 1
    enddo

!   bの初期化
    b = [(i, i = 1, size(b))]

!   初期解(予測解)
    x = b

!   gauss_sparse関数の呼び出し
    residue = gauss_sparse(num_iter = 100, &
                           tol = 1E-5, &
                           b = b, &
                           A = a, &
                           x = x, &
                           actual_iter = actual_iter)

!   出力
    print '(/ "A = ")'
    do i = 1, size(A, 1)
        print '(100f6.1)', A(i,:)
    enddo

    print '(/ "b = " / (f6.1))', b

    print '(/ "残差 = ", g10.3 / "反復回数 = ", i0 / "解 = "/ (11x, g10.3))', &
        residue, actual_iter, x

end program test_gauss_sparse

サブルーチンの記述

[編集]

手続きの引数を介して値を返す場合は、関数よりもサブルーチンを使用することが好まれます。これは、2つの配列の内容を入れ替えるサブルーチンの例によって示されます:

subroutine swap_real(a1, a2)

   implicit none

!  入力/出力
   real, intent(inout) :: a1(:), a2(:)

!  ローカル変数
   integer :: i
   real :: a

!  入れ替え
   do i = 1, min(size(a1), size(a2))
      a = a1(i)
      a1(i) = a2(i)
      a2(i) = a
   enddo

end subroutine swap_real

以前の例と同様に、このルーチンへの 明示的インターフェース は、その 型シグネチャ が呼び出し元で知られている必要があります。これは、関数を モジュール に配置し、呼び出しルーチンでモジュールを USE することによって、望ましい方法です。代替手段として、次の例に示すように INTERFACE ブロックを使用することもできます。

内部と要素手続き

[編集]

前の例の swap_real サブルーチンを書き直す別の方法は次の通りです:

subroutine swap_real(a1, a2)

   implicit none

!  入力/出力
   real, intent(inout) :: a1(:), a2(:)

!  ローカル変数
   integer :: N

!  内部サブルーチンを使用して入れ替え
   N = min(size(a1), size(a2))
   call swap_e(a1(:N), a2(:N))

 contains
   elemental subroutine swap_e(a1, a2)
      real, intent(inout) :: a1, a2
      real :: a
      a = a1
      a1 = a2
      a2 = a
   end subroutine swap_e
end subroutine swap_real

この例では、swap_e サブルーチンが要素であり、それは要素ごとの基準でその配列引数に作用します。要素手続きは 純粋 でなければならず(つまり、副作用を持たず、純粋な手続きのみを呼び出すことができます)、すべての引数はスカラーでなければなりません。swap_eswap_real サブルーチンの内部であるため、他のプログラムユニットはそれを呼び出すことはできません。

次のプログラムは、2つの swap_real サブルーチンのいずれかをテストするためのものです:

program test_swap_real
    implicit none

!   swap_realサブルーチンへの明示的なインターフェース
    interface
        subroutine swap_real(a1, a2)
            real, intent(inout) :: a1(:), a2(:)
        end subroutine swap_real
    end interface

!   変数の宣言
    integer :: i
    real :: a(10), b(10)

!   a、bの初期化
    a = [(real(i), i = 1, 20, 2)]
    b = a + 1

!   入れ替え前の出力
    print '(/"入れ替え前:")'
    print '("a = [", 10f6.1, "]")', a
    print '("b = [", 10f6.1, "]")', b

!   swap_realサブルーチンの呼び出し
    call swap_real(a, b)

!   入れ替え後の出力
    print '(// "入れ替え後:")'
    print '("a = [", 10f6.1, "]")', a
    print '("b = [", 10f6.1, "]")', b

end program test_swap_real

ポインタとターゲットメソッド

[編集]

Fortranにおいて、ポインタの概念は、Cのような言語とは異なります。Fortran 90のポインタは単にターゲット変数のメモリアドレスを格納するだけでなく、追加の情報も含みます。その情報には、ターゲットのランク、各次元の上限と下限、さらにはメモリを通過する際のストライドさえ含まれます。これにより、Fortran 90のポインタはサブ行列を指すことができます。

Fortran 90のポインタは、ポインタ代入演算子 (=>) または ALLOCATE 文を介して、明確に定義された ターゲット 変数と 関連付け されます。式中に現れる場合、ポインタは常にデリファレンスされます。 ポインタ演算 はできません。

次の例では、整数の配列を指すポインタを宣言し、そのポインタを介して配列の要素を変更します:

program pointer_example
    implicit none

!   ポインタの宣言
    integer, pointer :: ptr(:)

!   ターゲット配列の宣言
    integer, dimension(5) :: array

!   ポインタをターゲットに関連付ける
    ptr => array

!   ターゲット配列の要素を変更
    ptr(1) = 10
    ptr(2) = 20
    ptr(3) = 30
    ptr(4) = 40
    ptr(5) = 50

!   出力
    print *, "ターゲット配列の要素:"
    print *, array

end program pointer_example

このプログラムは、ターゲット配列の要素を直接変更するのではなく、ポインタを介して変更することができます。ポインタには => 演算子を使用してターゲットを指定します。

ポインタには 動的メモリ割り当て にも使用できます。下記に、ポインタを使用して動的に配列を割り当てる方法を示します:

program dynamic_allocation
    implicit none

!   ポインタの宣言
    integer, pointer :: ptr(:)

!   配列のサイズ
    integer :: size

!   ユーザーによる配列のサイズ入力
    print *, "配列のサイズを入力してください:"
    read *, size

!   ポインタを割り当て
    allocate(ptr(size))

!   ユーザーからの入力を配列に格納
    print *, "配列の要素を入力してください:"
    read *, ptr

!   出力
    print *, "入力された配列の要素:", ptr

!   割り当て解除
    deallocate(ptr)

end program dynamic_allocation

このプログラムでは、ユーザーによって配列のサイズが入力され、そのサイズの配列が動的に割り当てられます。その後、ユーザーが要素を入力し、それがポインタに格納された配列に保存されます。 最後に、ポインタのメモリが解放されます。

コメント

[編集]

これらの例は、Fortran 90/95の基本的な構文や機能を示しています。Fortranの機能は非常に豊富であり、これらの例でカバーされていない多くの機能がありますが、これらの例で紹介された構文や機能をマスターすることで、Fortranで広範なプログラミングを行う上での基盤を築くことができます。

モジュール

[編集]

Fortranのモジュールは、変数、関数、手続きなどの 公開インターフェース を定義するための便利な方法です。これにより、異なるプログラムユニット間で情報を共有することができます。たとえば、以下の例では、定数や手続きを含むモジュールを定義しています:

モジュールを使用するプログラムでは、次のようにしてモジュールをインポートします:

module GlobalModule

!  Reference to a pair of procedures included in a previously compiled
!  module named PortabilityLibrary
   use PortabilityLibrary, only: GetLastError, &  ! Generic procedure
                                 Date             ! Specific procedure
!  Constants
   integer, parameter :: dp_k = kind (1.0d0)      ! Double precision kind
   real, parameter :: zero = (0.)
   real(dp_k), parameter :: pi = 3.141592653589793_dp_k

!  Variables
   integer :: n, m, retint
   logical :: status, retlog
   character(50) :: AppName

!  Arrays
   real, allocatable, dimension(:,:,:) :: a, b, c, d
   complex(dp_k), allocatable, dimension(:) :: z

!  Derived type definitions
   type ijk
      integer :: i
      integer :: j
      integer :: k
   end type ijk

   type matrix
     integer m, n
     real, allocatable :: a(:,:)  ! Fortran 2003 feature. For Fortran 95, use the pointer attribute instead
   end type matrix

!  All the variables and procedures from this module can be accessed
!  by other program units, except for AppName
   public
   private :: AppName

!  Generic procedure swap
   interface swap
      module procedure swap_integer, swap_real
   end interface swap

   interface GetLastError  ! This adds a new, additional procedure to the
                           ! generic procedure GetLastError
      module procedure GetLastError_GlobalModule
   end interface GetLastError

!  Operator overloading
   interface operator(+)
      module procedure add_ijk
   end interface

!  Prototype for external procedure
   interface
      function gauss_sparse(num_iter, tol, b, A, x, actual_iter) result(tol_max)
         real ::  tol_max
         integer, intent(in) :: num_iter
         real, intent(in) :: tol
         real, intent(in), dimension(:) :: b, A(:,:)
         real, intent(inout) :: x(:)
         integer, optional, intent(out) :: actual_iter
      end function gauss_sparse
   end interface

!  Procedures included in the module
   contains

!  Internal function
   function add_ijk(ijk_1, ijk_2)
     type(ijk) add_ijk, ijk_1, ijk_2
     intent(in) :: ijk_1, ijk_2
     add_ijk = ijk(ijk_1%i + ijk_2%i, ijk_1%j + ijk_2%j, ijk_1%k + ijk_2%k)
   end function add_ijk

!  Include external files
   include 'swap_integer.f90' ! Comments SHOULDN'T be added on include lines
   include 'swap_real.f90'
end module GlobalModule