Fortran/Fortranの活用例
以下の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
文に流れ込んだ場合、それはノーオペレーションとなります。したがって、上記の例ではWRITE
とSTOP
の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
上記の例は以下を示すために意図されています:
- 上記の
PRINT
とREAD
文では、'*
'をフォーマットとして使用しており、リスト指向のフォーマットを指定しています。リスト指向のフォーマットは、次の引数に基づいて必要な入力または出力のフォーマットをコンパイラに推測させるようにします。 - FORTRANの初期のマシンでは文字セットが制限されていたため、FORTRAN 77では関係演算子 =、≠、<、>、≤、および ≥ を表す
.EQ.
、.NE.
、.LT.
、.GT.
、.LE.
、.GE.
のような略記が使用されます。 - この例では、暗黙の型指定機構を使用して、
NA
、NB
、IA
、IB
、およびITEMP
のINTEGER型を指定します。 - 関数
NGCD(NA, NB)
では、関数引数NA
とNB
の値がそれぞれローカル変数IA
とIB
にコピーされます。これは、IA
とIB
の値が関数内で変更されるために必要です。Fortranの関数およびサブルーチンでの引数の受け渡しはデフォルトで参照渡しを利用します(値渡しではなく、例えばCのような言語のデフォルトとは異なります)。関数内からNA
とNB
を修正すると、関数を呼び出したメインの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_e
が swap_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