FORTRAN 77の言語仕様

FORTRAN 77の言語仕様（フォートランななじゅうななのげんごしよう）は、FORTRAN 66とFORTRAN 77の言語仕様と、言語仕様にまつわるエピソードについて解説している。なお、Fortran 90以降の言語仕様については、Fortranの言語仕様を参照のこと。 Fortranの言語仕様は、年代によってかなり変化して来ている。他のプログラミング言語で実装された構造化プログラミングの機能などがどんどん取り入れられて来ているからである。 FORTRAN 66の言語仕様はおおよそ下記のとおり。 英数字(大文字) 特殊文字（空白　=　+　-　*　/　(　)　,　.　'　:　;　&　%　$　#　"　@） プログラムは以下の形式で記述する。もともとパンチカードに書くことを前提としていたので、1行は80文字で構成されていた。行の左端からの文字位置を桁（けた）またはカラムと呼んだ。 もともとのFORTRANは、字句解析の仕組みがCやJavaと大きく異なり、桁位置（行の左端からの文字位置）に依存していた。これを固定プログラム形式という。これは、当時、データの入力にパンチカードを 使用していたためである。1桁目から5桁目が文番号で、6桁目が継続行の指定、7桁目から72桁目に文の本体を記述する。73桁目から80桁目は任意に指定可能な、行を識別するための文字列を記述する領域である。また、文字列定数中以外の空白文字は一切無視されていた。二つ以上の字句を空白無しで続けて書 いたり、逆に一つの字句の途中に空白文字を入れても構わない。 1～5桁目 文番号を書く。1カラム目にCを書くとその行はコメント行になる。 6桁目 継続行であるときはここに任意の文字（空白又は0以外）を書く 7～72桁目 本文を書く 73～80桁目 シーケンシャル番号を書く（行を識別するための番号、本文には影響しない） 物理的に穴を開けるパンチカードのほかに、鉛筆で塗りつぶすマークシート方式も存在した。消しゴムで消せば楽に修正できるわけである。いずれも、カード1枚がプログラム1行に相当する。これらは「FORTRANカード」と呼ばれた。 以下の数を扱える。 整数（範囲は機種依存、4バイトであればその範囲） 単精度実数（整数部+小数部、指数表示可能、精度は機種依存） 倍精度実数（単精度の倍の精度を持つ） 複素数（単精度または倍精度の実数の組み合わせ。2つの数字を()でくくる） 論理型（.TRUE. または .FALSE. 両脇にドットがある） 文字型（桁数のあとにHを記述し、その後に文字列を記述する。例えば 7HFORTRAN など） 以下の変数宣言を扱える。 暗黙の型宣言 整数型（変数名の先頭がI～Nのもの） 実数型（上記以外） 明示的な型宣言 INTEGER宣言文 REAL宣言文 DOUBLE宣言文 COMPLEX宣言文 LOGICAL宣言文 配列（宣言文のあとに()をつけ、要素数を記述する） 算術式（+、-、*、/、**（べき乗）） 比較（.LT.、.LE.、.GT.、.GE.、.EQ.、.NE. 両脇にドットがある） 論理演算（.OR.、.AND.、.NOT. 両脇にドットがある） 実行文 代入文 ASSIGN文 ASSIGN文で変数に割り当て、次の割当て型GO TO文で割り当てられた文番号にジャンプする。 制御文 単純GO TO文 計算型GO TO文 飛び先がリストされていて、その後にある変数の内容を評価し、その値によって飛び先にジャンプする。 割当て型GO TO文 ASSIGN文で割り当てられた飛び先にジャンプする。 算術IF文 値を評価し、その値がマイナス、0、プラスによって3方向にジャンプする。 論理IF文 現在主流のプログラミング言語では、ごく一般的なif文。値の真偽値に基づき、真なら命令文を実行する。 CALL文 サブルーチン副プログラムを呼び出す。 RETURN文 サブルーチン副プログラムで、呼出し元に戻る。 DO文 繰り返しを構成する。制御変数は整数型のみ。 CONTINUE文 それ自身では何もしない。通常、DO文の終端を示すのに使われる。 STOP文 プログラムの実行を終了する。 PAUSE文 プログラムを一旦中止し、コンソール上にメッセージを出す。記述できるメッセージは数字のみ 入出力文 READ文 デバイスと書式を指定し、リストされた変数に値を入力する。デバイスは数字。5がカードリーダ入力、6がラインプリンタにあらかじめ指定されていることが多い。書式無しのREAD文もある WRITE文 READ文の逆で、リストされた変数の内容を書式に従って出力する。 補助入出力文 磁気テープ装置の入出力を行いやすくするために使われるのが基本。 REWIND文 読み出し又は書き込み位置を装置の先頭に位置づける。 BACKSPACE文 一つ前の記録位置に位置づける。 END FILE文 ファイルエンドマークを記録する。 宣言文 DIMENSION文 配列を定義する。 COMMON文 プログラム間での共通領域を定義する。 EQUIVALENCE文 ある変数と別の変数を同じ物とみなす。 EXTERNAL文 外部手続きであることを宣言する。 型宣言文 型宣言文は配列定義も兼ねられる。 INTEGER文 REAL文 DOUBLE PRECISION文 COMPLEX文 LOGICAL文 DATA文 初期データを定義する場合に利用する。 FORMAT文 各種の書式を定義する。 型変換 I変換 整数の変換 F変換 小数点付きの実数の変換 E変換 指数表示の実数の変換 D変換 指数表示の倍精度実数の変換 G変換 桁数に応じて、EまたはF変換 L変換 論理型の変換 H変換 桁数指定の文字列の変換。プリンタに出力する場合、行の最初の桁が改行指示となる。 + 行送りなし（重ね打ち） 空白 1行送り（通常） 0 2行送り（1行飛ばし） 1 改ページ A変換 文字列の変換 副プログラム関連 FUNCTION文 SUBROUTINE文 BLOCK DATA文 以下の関数を扱える。 文関数 そのプログラム内で有効な関数定義を1行で記述する。 組込み関数 組込み関数は、同じ機能でも引数の型と関数の型によって名前が異なる。関数名の先頭がI～Nのものが整数型、Dが倍精度実数型、Cが複素数型、それ以外が実数型である。 絶対値（IABS、ABS、DABS） 切捨て（INT、AINT、DINT） 剰余（MOD、AMOD） 最大値（MAX0、MAX1、AMAX0、AMAX1、DMAX1） 最小値（MIN0、MIN1、AMIN0、AMIN1、DMIN1） 実数化（FLOAT） 整数化（IFIX） 符号の付け替え（ISIGN、SIGN、DSIGN） 超過分（IDIM、DIM） 単精度実数化（SNGL） 倍精度実数化（DBLE） 複素数の実数部（REAL） 複素数の虚数部（AIMAG） 複素数化（CMPLX） 共役複素数（CONJG） 外部関数 あらかじめライブラリとして提供されている関数 関数副プログラム 関数副プログラムは、FUNCTION文で始まるサブルーチンである。 平方根（SQRT、DSQRT、CSQRT） 指数（EXP、DEXP、CEXP） 自然対数（ALOG、DLOG、CLOG） 常用対数（ALOG10、DLOG10） 正弦（SIN、DSIN、CSIN） 余弦（COS、DCOS、CCOS） 逆正接（ATAN、DATAN、ATAN2、DATAN2） 剰余（DMOD） 絶対値（CABS） FORTRAN 77は、FORTRAN 66に、よりプログラムを書きやすくするための文や、動作の明確化をはかっている。FORTRAN 66でのプログラムはほぼそのまま動くようになっている。以下はFORTRAN 77で追加/変更になった点の概要である。 文字列を指定するのに、アポストロフィ（'）が使えるようになった。例えば'FORTRAN'。 変数などについて以下の変更が加えられた。 IMPLICIT文で、暗黙の型宣言の範囲を変更できるようになった。一部のコンパイラではすでに実装されていたものを仕様化したものである。整数、実数だけではなく、文字型なども指定できる。 PARAMETER文で、定数に名前を与えられるようになった。FORTRAN 66では、定数は、直接式に書くか、変数を定数代わりに使う方法しかなかった。前者は定数の変更に手間がかかり、後者は定数を変更されてしまう危険性があった。 配列の宣言に寸法宣言子を指定できるようになった。FORTRAN 66では配列は1から始まるだけだったが、FORTRAN 77ではマイナスの領域も定義できるようになった。たとえば以下のように定義できる。 DIMENSION ARRAY (-10:10) 部分文字列が利用できるようになった。配列と同じような形で指定できる。 CHARACTER*4 CHR CHR(1:2) = "2B" 配列の初期化にDO形並びが利用できるようになった 型の異なる変数/定数間での型の変換方法が定義された(?) 文字の連結演算子(//)が定義された 論理演算子に、等しければ真となる.EQV.と、異なれば真となる.NEQV.（XORと同じ）が定義された 宣言文 IMPLICIT文が追加された （関数）副プログラムで、そのプログラム内で定義した変数を、そのプログラム内から抜け出たときも値が不定にならないように、保存するSAVE文が追加された。 主プログラム名を指定するPROGRAM文が追加された。 実行文 IF構造として、IF THEN～ と IF THEN～ELSE～END が利用できるようになった。 入出力文 入出力文の書式が大幅に強化された。以下のパラメータが利用できるようになった。これらを使うことで、入出力操作の柔軟性が向上した。 UNIT=（装置指定子） FMT=（書式指定子） ERR=（誤り指定子） END=（ファイル終了指定子） IOSTAT=（入出力状態指定子） REC=（記録指定子） 補助入出力文に以下の文が追加された OPEN文 CLOSE文 INQUIRE文 以下の関数が追加された。 文字長(LEN) 部分列の位置（INDEX） 正接（TAN、DTAN） 逆正接（ASIN、DASIN） 逆余弦（ACOS、DACOS） 逆正接（ATAN、DATAN） 双曲線正弦（SINH、DSINH） 双曲線余弦（COSH、DCOSH） 双曲線正接（TANH、DTANH） 文字列の大小比較（LGE、LGT、LLE、LLT） FORTRANのプログラムにおいては、とくに型宣言をしない場合、アルファベットI,J,K,L,M,Nのいずれかで始まる変数名で表される変数は整数型（integer）になり、その他のアルファベット（A～H, O～Z）で始まるものは実数型（real）になる。これが「暗黙の型宣言」といわれるFORTRANの文法上の規則である。なお、この命名規則についてBakusは『Iから始まる6文字に決まったのは、みんな添え字にI、J、Kをつかっていたので、(L、M、Nを)気前よく増やした。』と言っている。 FORTRANのプログラムでは、特にループ構造を表すDO構文におけるカウンター変数としてアルファベットI～Nの1文字の変数名が伝統的によく使用されており、プログラミングにおける慣習上の変数命名規則の一つとなっている。i, j,...の1文字変数をインデックス参照などに使われるループカウンタに使うことは、他の多くのプログラミング言語に伝搬したが、プログラミング言語における慣習としてはFORTRANに由来する（数学の総和などでもiを使う慣習があるので、プログラミング言語以外について含めたら、必ずしもFORTRANが元祖とは言い切れない）。 いくつかの観点から、変数には明示的な宣言が必要とされることもある。Fortran 90以降ではIMPLICIT NONE文により暗黙の型宣言を無効にできるようになった。コンパイラのエラーメッセージや警告により暗黙の型宣言に起因するバグの発見が容易になるので積極的に用いるべきである。 FORTRANの「空白は無視する」という言語仕様が招く、コンマとピリオドの打ち間違いがコンパイルエラーにならず、間違ったプログラムになる、という例を説明する。 Cで、次のように書くプログラムの一部があったとする。 int i; (中略) for( i=1; i<=5; i++ ) { 何らかの処理; } これをFORTRAN66で書くと次のように書ける。 DO 10 I=1,5 何らかの処理 10 CONTINUE 最初の行は、整数型変数Iを1から5まで1ずつ増加させつつ、行ラベル10の行までを繰り返し実行することを表す。行ラベル10のCONTINUE文はループ制御の端末文ではなく、どこにでも置ける「何もしない」機能の文である。 この、DO文のコンマをピリオドに打ち間違えたとする。すると、空白を無視するFORTRAN66では、この行は、 DO10I=1.5 という、「DO10Iという実数型変数に1.5という実数を代入する代入文」と解釈される。FORTRANでは変数宣言が無い場合、DO10Iは実数型変数を暗黙に示すからである。 この結果、このプログラムは（他のどこからも参照されない）変数DO10Iへの代入とただ1回の「何らかの処理」が実行されるだけとなり、意図した繰り返し処理は起こらない。 C言語などでは、空白文字は字句要素の切れ目として解釈されるため、DO と 10 と Iが結合して一つの字句要素になることはない。 「これが原因でNASAの宇宙ロケットが制御不能になり爆破された」という話が度々語られる。しかしこの話は、1960年代前半のほぼ同時期にあった、二つの異なった事件の混同による、ある種の「都市伝説」である。 要約すると、 FORTRANの言語仕様が原因で生じたバグは、実機ではなくソフトウェアだけでの実行結果の異常として発覚し修正されたため、実際のロケット打ち上げには関係していない。これはマーキュリー計画において起きた。 宇宙機のロストにつながったプログラムのミスは言語仕様とは関係なく、プログラムするべき、として示された数式において、必要な線が欠けていたため発生した。これはマリナー1号において起きた。プログラミング（コーディング）に間違いは無くても、要求された仕様の時点でその中にバグがあるかもしれない、という教訓となっている。 DO文によるバグは、1963年、マーキュリー宇宙船の軌道計算ソフトウェアを開発中に、スタッフの一人が計算精度に問題があることに気づいた。コードを調査した結果、「DO 10 I=1.10」という行を発見した。それが原因で、ループにより近似の精度を高めるはずのコードが、ループしないため精度を高めることなく通り過ぎてしまっていた。これを「DO 10 I=1,10」と修正したところ、期待した精度が得られるようになった。 宇宙機の喪失につながった事故は、1962年、金星探査機マリナー1号を打ち上げるアトラスロケットには、Rate SystemおよびTrack Systemと呼ばれる二種類の制御系統が搭載されていた。Rate Systemは搭載型であり、地上型のTrack Systemは前者に対するバックアップとして動作する。1962年7月、マリナー1号の打ち上げ直後に、ロケットのguidanceアンテナは必要な性能を発揮しなくなり、搭載型システムのみに頼ってロケットは制御されることになった。しかし搭載型システムには次のような仕様バグがあった。「このようにプログラムすべき」として指示されていた手書きの数式中の、平均化を示す線（しばしば「ハイフン」と言及されるが、正確にはオーバーライン）が欠落しており、そのため平均化した値を使うべきところでそのままの値が使われ、微妙なタイミング差の平滑化処理に問題があった（プログラム自体は「指示された仕様の通り」正しく書かれていた。指示された仕様が間違っていたのである）。結果として、制御コンピュータは、ロケットが正常に上昇しているにもかかわらず、異常な補正動作をロケットに指示し続け、正常なコースから外れていった。結局、ロケットには破壊指令が送られ自爆し、マリナー1号は大西洋へと沈んだ。