Fortran
現代の歴史的な機能の代替
サーチ…
暗黙の変数型
Fortranが最初に開発されたとき、メモリはプレミアムでした。変数とプロシージャー名には最大6文字を使用でき、変数には暗黙的に入力されることがよくあります。これは、変数名の最初の文字がその型を決定することを意味します。
- i、j、...、nで始まる変数は
integer
- 他のすべて(a、b、...、h、o、p、...、z)は
real
次のようなプログラムが許容されます。
program badbadnotgood
j = 4
key = 5 ! only the first letter determines the type
x = 3.142
print*, "j = ", j, "key = ", key, "x = ", x
end program badbadnotgood
implicit
ステートメントで独自の暗黙ルールを定義することさえできます:
! all variables are real by default
implicit real (a-z)
または
! variables starting with x, y, z are complex
! variables starting with c, s are character with length of 4 bytes
! and all other letters have their default implicit type
implicit complex (x,y,z), character*4 (c,s)
暗黙の型指定はもはやベストプラクティスとみなされます。タイプミスが気付かれないことがあるので、暗黙の型定義を使用して間違いを作るのはとても簡単です。
program oops
real :: somelongandcomplicatedname
...
call expensive_subroutine(somelongandcomplEcatedname)
end program oops
このプログラムは楽しく実行され、間違ったことをします。
暗黙の型指定を無効にするには、 implicit none
ステートメントを使用できます。
program much_better
implicit none
integer :: j = 4
real :: x = 3.142
print*, "j = ", j, "x = ", x
end program much_better
私たちが使用していなかった場合はimplicit none
プログラムでoops
以上、コンパイラはすぐに気づいた、とエラーが発生しているだろう。
算術ifステートメント
算術式if
文は、算術式の結果に応じて3つの分岐を使用できる
if (arith_expr) label1, label2, label3
このif
文は、制御フローをコード内のラベルの1つに転送します。 arith_expr
の結果が負の場合はlabel1
が関与し、結果がゼロの場合はlabel2
が使用され、結果が正の場合は最後のlabel3
が適用されます。算術演算if
、すべての3つのラベルが必要ですが、それゆえ、この文は2つの分岐に簡略化することができ、ラベルの再利用を許可するif
。
例:
if (N * N - N / 2) 130, 140, 130
if (X) 100, 110, 120
現在、この機能は廃止されており、 if
文とif-else
構文によって同じ機能が提供されていif-else
。例えば、フラグメント
if (X) 100, 110, 120
100 print*, "Negative"
goto 200
110 print*, "Zero"
goto 200
120 print*, "Positive"
200 continue
if-else
構文として書くことができます
if (X<0) then
print*, "Negative"
else if (X==0) then
print*, "Zero"
else
print*, "Positive"
end if
if
ステートメントの置換
if (X) 100, 100, 200
100 print *, "Negative or zero"
200 continue
多分
if (X<=0) print*, "Negative or zero"
非ブロックDO構造
非ブロックdo
構造は次のようになります
integer i
do 100, i=1, 5
100 print *, i
つまり、ラベル付き終了文がcontinue
文ではない場合です。終了ステートメントとして使用できるステートメントにはさまざまな制限があり、全体的には非常に混乱します。
このような非ブロック構造は、ブロック形式で次のように書き直すことができます。
integer i
do 100 i=1,5
print *, i
100 continue
end do
ステートメントを使用して、
integer i
do i=1,5
print *, i
end do
代替リターン
代替リターンは、サブルーチンからの復帰時に実行フローを制御する機能です。これはしばしばエラー処理の一種として使われます:
real x
call sub(x, 1, *100, *200)
print*, "Success:", x
stop
100 print*, "Negative input value"
stop
200 print*, "Input value too large"
stop
end
subroutine sub(x, i, *, *)
real, intent(out) :: x
integer, intent(in) :: i
if (i<0) return 1
if (i>10) return 2
x = i
end subroutine
代替リターンは、サブルーチン仮引数リストの引数*
によってマークされます。
*100
と*200
上のcall
文では、それぞれ100
と200
というラベルのステートメントを参照してください。
サブルーチン自体では、代替リターンに対応するreturn
文には番号があります。この数値は戻り値ではなく、実行時に返される提供ラベルを示します。この場合、 return 1
は実行を100
というラベルのステートメントに渡し、 return 2
は実行を200
というラベルのステートメントに渡します。飾り気のないreturn
のない声明、またはサブルーチンの実行の完了return
声明、passess実行にすぐに呼び出し文の後に。
代替の戻り構文は、他の形式の引数関連とは非常に異なり、この機能は現代の趣旨に反してフロー制御を導入します。整数の「ステータス」コードが返され、より快適なフロー制御が管理できます。
real x
integer status
call sub(x, 1, status)
select case (status)
case (0)
print*, "Success:", x
case (1)
print*, "Negative input value"
case (2)
print*, "Input value too large"
end select
end
subroutine sub(x, i, status)
real, intent(out) :: x
integer, intent(in) :: i
integer, intent(out) :: status
status = 0
if (i<0) then
status = 1
else if (i>10)
status = 2
else
x = i
end if
end subroutine
固定ソースフォーム
Fortranはもともと、80列のパンチカードをベースにした固定書式用に設計されていました。
はい:これは著者自身のコードの行です
これらは、次のようにカードパンチマシンで作成されました。
画像は著者によるオリジナル写真です
図示のサンプルカードに示されているように、書式ラベルの最初の5列が予約されています。最初の列は、文字Cでコメントを示すために使用された。 6番目の列は、文の継続を示すために使用されます(0以外の文字を挿入することによって)。最後の8列は、カードの識別と順序付けに使用されました。これは、カードのデッキを床に落とした場合にはかなり価値がありました!穿孔されたカードの文字コーディングには、限られた文字セットしかなく、大文字のみでした。その結果、Fortranプログラムは次のようになりました。
DIMENSION A(10) 00000001
C THIS IS A COMMENT STATEMENT TO EXPLAIN THIS EXAMPLE PROGRAM 00000002
WRITE (6,100) 00000003
100 FORMAT(169HTHIS IS A RATHER LONG STRING BEING OUTPUT WHICH GOES OVE00000004
1R MORE THAN ONE LINE, AND USES THE STATEMENT CONTINUATION MARKER IN00000005
2COLUMN 6, AND ALSO USES HOLLERITH STRING FORMAT) 00000006
STOP 00000007
END 00000008
スペース文字は、 ホレリス文字定数の中を除いて(上記のように)どこでも無視されました。これは、スペースが予約語や定数の内部で発生する可能性があること、または完全に欠落していることを意味していました。これには、以下のような誤解を招くような文章の副作用がありました。
DO 1 I = 1.0
変数DO1I
への代入でDO1I
、
DO1I = 1,0
実際には変数I
上のDO
ループです。
現代のFortranでは、この固定形式の入力は不要であり、任意の列を使用した自由形式が可能です。コメントは!
示されます!
ステートメント行に追加することもできます。スペースは現在どこでも許可されておらず、他のほとんどの言語と同様にセパレータとして使用する必要があります。上記のプログラムは、現代のFortranで次のように記述することができます:
! This is a comment statement to explain this example program
Print *,"THIS IS A RATHER LONG STRING BEING OUTPUT WHICH no longer GOES OVER MORE THAN ONE LINE, AND does not need to USE THE STATEMENT CONTINUATION MARKER IN COLUMN 6, or the HOLLERITH STRING FORMAT"
古いスタイルの継続はもう使用されませんが、上記の例は、非常に長いステートメントが引き続き発生することを示しています。現代のFortranは、最後と最後に&
使用します。例えば、上記をより読みやすい形式で書くことができます:
! This is a comment statement to explain this example program
Print *,"THIS IS A RATHER LONG STRING BEING OUTPUT WHICH still &
&GOES OVER MORE THAN ONE LINE, AND does need to USE THE STATEMENT &
&CONTINUATION notation"
共通ブロック
初期のFortran形式では、サブルーチンと関数からグローバル変数ストアを作成するための唯一のメカニズムは、 COMMON
ブロックメカニズムを使用することです。これにより、一連の変数を名前にして共有することができました。
名前付きの共通ブロックに加えて、空白(無名)の共通ブロックもあります。
空白の共通ブロックは次のように宣言できます
common i, j
名前付きブロックvariables
は次のように宣言できます
common /variables/ i, j
完全な例として、値を追加および削除できるルーチンで使用されるヒープストアを想像することができます。
PROGRAM STACKING
COMMON /HEAP/ ICOUNT, ISTACK(1023)
ICOUNT = 0
READ *, IVAL
CALL PUSH(IVAL)
CALL POP(IVAL)
END
SUBROUTINE PUSH(IVAL)
COMMON /HEAP/ ICOUNT, ISTACK(1023)
ICOUNT = ICOUNT + 1
ISTACK(ICOUNT) = IVAL
RETURN
END
SUBROUTINE POP(IVAL)
COMMON /HEAP/ ICOUNT, ISTACK(1023)
IVAL = ISTACK(ICOUNT)
ICOUNT = ICOUNT - 1
RETURN
END
共通ステートメントを使用して、変数の型を暗黙的に宣言し、 dimension
属性を指定することができます。この動作だけでは、しばしば混乱の原因になります。さらに、暗黙的なストレージの関連付けおよびプログラム単位にわたる繰り返し定義の要件により、共通のブロックを使用してエラーが発生しやすくなります。
最後に、共通ブロックは、そのブロックに含まれるオブジェクトの中で非常に制限されています。たとえば、共通ブロック内の配列は明示的なサイズでなければなりません。割り当て可能なオブジェクトは発生しない可能性があります。派生型はデフォルトの初期化を持つことはできません。
現代のFortranでは、変数の共有はモジュールの使用によって処理できます 。上記の例は次のように書くことができます:
module heap
implicit none
! In Fortran 2008 all module variables are implicitly saved
integer, save :: count = 0
integer, save :: stack(1023)
end module heap
program stacking
implicit none
integer val
read *, val
call push(val)
call pop(val)
contains
subroutine push(val)
use heap, only : count, stack
integer val
count = count + 1
stack(count) = val
end subroutine push
subroutine pop(val)
use heap, only : count, stack
integer val
val = stack(count)
count = count - 1
end subroutine pop
end program stacking
名前付きブロックと空白の共通ブロックはわずかに異なる動作をします。注:
- 名前付き共通ブロック内のオブジェクトを最初に定義することができます。ブランクコモンのオブジェクトは
- 空白の共通ブロック内のオブジェクトは、共通ブロックに
save
属性があるかのように動作します。ブロックがアクティブなプログラム単位のスコープ内にない場合、save
属性のない名前付き共通ブロック内のオブジェクトは未定義になることがあります
この後者の点は、現代のコードにおけるモジュール変数の挙動と対比させることができる。 Fortran 2008のすべてのモジュール変数は暗黙的に保存され、モジュールが有効範囲外になると未定義になりません。 Fortran 2008のモジュール変数(名前付き共通ブロック内の変数など)も、モジュールが有効範囲外になったときには未定義になります。
割り当てられたGOTO
割り当てられたGOTOは、ASSIGN文を使用してステートメントラベルが割り当てられる整数変数を使用します。
100 CONTINUE
...
ASSIGN 100 TO ILABEL
...
GOTO ILABEL
割り当てられたGOTOはFortran 90では廃止され、Fortran 95以降では削除されています。プロシージャ、内部プロシージャ、プロシージャポインタなどの機能を使用することで、現代のコードでは回避できます。
計算されたGOTO
計算されたGOTOは、整数式の値に従ってプログラムの分岐を可能にします。
GOTO (label_1, label_2,... label_n) scalar-integer-expression
scalar-integer-expression
が1に等しい場合、プログラムはステートメントラベルlabel_1
で継続し、2の場合はlabel_2
します。 1
より小さいか、またはn
より大きい場合、次の行にプログラムが続きます。
例:
ivar = 2
...
GOTO (10, 20, 30, 40) ivar
ステートメントラベル20にジャンプします。
この形式のgoto
は、Fortran 95以降では廃止されており、 select case
構文に取って代わられていselect case
。
割り当てられた書式指定子
Fortran 95より前には、入力または出力に割り当てられたフォーマットを使用することができました。検討する
integer i, fmt
read *, i
assign 100 to fmt
if (i<100000) assign 200 to fmt
print fmt, i
100 format ("This is a big number", I10)
200 format ("This is a small number", I6)
end
assign
ステートメントは、ステートメントラベルを整数変数に割り当てます。この整数変数は、後でprint
ステートメントの書式指定子として使用されます。
このような書式指定子の割り当てはFortran 95で削除されました。代わりに、より現代的なコードでは、他の形式の実行フロー制御
integer i
read *, i
if (i<100000) then
print 100, i
else
print 200, i
end if
100 format ("This is a big number", I10)
200 format ("This is a small number", I6)
end
または文字変数を書式指定子として使用できます
character(29), target :: big_fmt='("This is a big number", I10)'
character(30), target :: small_fmt='("This is a small number", I6)'
character(:), pointer :: fmt
integer i
read *, i
fmt=>big_fmt
if (i<100000) fmt=>small_fmt
print fmt, i
end
ステートメント関数
プログラムを検討する
implicit none
integer f, i
f(i)=i
print *, f(1)
end
ここで、 f
はステートメント関数です。整数結果の型を持ち、1つの整数の仮引数をとります。 1
そのようなステートメント関数は、それが定義されているスコープ内に存在します。特に、変数とそのスコープ内でアクセス可能な名前付き定数にアクセスできます。
しかし、ステートメント関数には多くの制限があり、混乱を招く可能性があります(配列要素代入文のようなカジュアルな見方を参照)。重要な制限事項は次のとおりです。
- 関数の結果と仮引数はスカラーでなければなりません
- 仮引数は関数と同じスコープにあります
- 文関数にはローカル変数がありません
- 文の関数を実際の引数として渡すことはできません
ステートメント関数の主な利点は、内部関数
implicit none
print *, f(1)
contains
integer function f(i)
integer i
f = i
end function
end
内部関数は、内部の副プログラムがそれ以上の内部副プログラムを含んでいないことに注意する価値があるかもしれませんが、上記の制限の対象ではありません(しかし、文機能を含むかもしれません)。
内部関数には独自のスコープがありますが、使用可能なホストの関連付けもあります。
1実際の古いコード例では、結果に明示的な型があっても、暗黙的に型指定されている文関数の仮引数を見るのは珍しいことではありません。