Fortran 標準コーディングルール
2005年12月1日改訂
目次
1.はじめに
2.スタイルルール(推奨リスト)
3.Fortran90の機能を生かして
4.禁止事項
5.現段階では規定していないが重要なもの
付録 気象予報モデル・データ同化プログラムのためのコメント
参考文献
変更履歴
ヨーロッパや米国の気象機関では、プログラムのソースコードを理解しやすく 管理しやすいものにし、またソースコードの交換を容易にするため、プログラミ ングルールが作成されている。日本でもこれらを参考にして、気象庁数値予報課 と気象研究所気候研究部・予報研究部が中心となって日本版標準コーディングル ールを作成している。その目的は、プログラミング・スタイルを統一することに より、可読性と移植性を高め、維持管理を容易にすることである。この目的に賛 同する開発担当者は、このルールに従いプログラムを作成し、同時にこのルール を改善するよう努力してほしい。
このプログラミングルールではFortran90 の使用を前提としている。Fortran 90 は、 FORTRAN77 と互換性を保ちながら様々な機能を付加したもので、従来と比 較して多くの点で改良されている。最新の JIS 規格は Fortran 95 であるが、 Fortran 90 しか使えない環境が依然多い状況であると思われるので、Fortran 95 の新機能は当面使わないことを推奨する。
対象とするプログラムとして、気象予報モデルとデータ同化モデルを想定して はいるが、高速で実行する必要がある大規模プログラムを多人数で共同開発する 場合には、異分野でも役に立つと考えている。
一部のルールについては断定的に記述していない部分がある。今後 Fortran 90 についてより多くの経験をつむことにより、今後の改訂版では追加・削除・変更 されることを了解していただきたい。
7-72桁に文を書かなければならない等の制約がなくなり、それ以外の桁位置 に書いた文字が無視されるなどの誤りを防ぐことができる。
プログラム単位とは、主プログラム、外部関数、外部サブルーチン、モジュ
ール、BLOCKDATA のいずれかである。たとえば、外部サブルーチン SUB は
sub.f90 に保存されるべきである。
こうしておけばプログラムを読むときにファイルを探しやすい。また、サブ
ルーチンを書き換えた場合に他のプログラム単位をコンパイルしなおすこと
がなくなるので、コンパイル時間が短くなる。
いつも主プログラムは program 文ではじめること。また end 文としては "end program name" や "end subroutine name" 等の形を使 用すること。
継続行を追加する場合の継続記号(&:アンド記号)は、行末(開始行の 終わりの文字として)だけではなく行頭(継続行の最初の文字として)にも つけること。継続行であるということがはっきりと区別できるようになる。
読みやすさのため、2桁づつの幅で字下げすること。字下げとはプログラム
の構造上ひとまとまりとなる文をそのほかの文に比べて2字右側の桁位置か
ら書き始めることである。
字下げを行う「まとまり」とはプログラム単位(subroutine)、引用使用宣
言(interface)、実行部ブロック(do, if など)である。これらの終わり
の文はすべて「end」で始まる文なので、「end まで字下げ」と覚えれば簡
単である。
字下げされた範囲の中の注釈行も字下げすること。これによって、行頭の空
白でない最初の桁位置を追うだけでプログラムのアルゴリズム構造が見出せ
るようになる。
継続行は開始行に対して2字字下げすること。
読みやすさのため、名前と演算子など、字句の間に適切な空白を入れ、同じ スタイルを一貫して用いること。英文タイプの句読法に従うのが望ましい。
またブロックではない一群の文は前後の文との間に空行を入れて区別すること。
空白は原則として1個とするが、似たような構成の代入文が連続するときに
余分な空白をいれて等号の桁位置をそろえ、類似性を見比べやすくするなど
してもよい。
すべてのプログラム単位の冒頭(use 文があるときはその直後) に implicit none 文を書くこと。そしてすべての変数・定数は明示的に宣言すること。 これによって、宣言されていない名前を用いるとコンパイルエラーを起こす ので、名前のつづり間違えによるバグを防ぐことができる。
プログラムがわかりにくくなる。
変数名やサブルーチン名は、その役割に基づいた名前をつけておくとプログ ラムを読む際にその意味や構造を把握しやすい。
FORTRAN 77 では型宣言文と属性を与えるための文 (SAVE, DATA, DIMENSION, PARAMETER, など) が別であったため、同じ名前の列を何度も書く必要が あった。Fortran 90 では型宣言文で属性をすべて設定できるようになった ため、すべての属性を型宣言文で指定すること。たとえば以下の3つのコー ドはまったく等価である。
昔ながらの書き方 |
INTEGER N DIMENSION N(2) SAVE N DATA N / 400, 224 |
◎推奨する書き方 |
integer, save:: n(2) = (/400, 224/) |
×推奨しない書き方 |
integer, save, dimension(2):: n = (/400, 224/) |
上記例3行目のような、型宣言文の dimension 属性は使わないこと。 代わりに2行目のような書き方を用いること。
識別を容易にするためintent(in), intent(out), intent(inout) をつける。
例えば intent(in) がつけられた変数の値を変更する文が存在すると、コン
パイル時にエラーが発生し、デバッグを助ける。
なお、手続のモジュール化または interface ブロックによって引用仕様を
明示することにより、さらにその恩恵を受けることが可能である。
文番号を伴ったformat 文は禁止する。代わりに入出力文の fmt 指定子に記 述すること。
プログラムの行数が少なくなるので、可能であれば使用すること。配列か変 数かの区別をつけて読みやすくするため、カッコ内に配列の形状を示すこと。
昔ながらの書き方 |
DIMENSION(N) X, Y DO 300 I = 1, N X(I) = Y(I) 300 CONTINUE |
◎推奨する書き方 |
real:: x(n), y(n) x(1:n) = y(1:n) |
×推奨しない書き方 |
x(:) = y(:) または x = y |
並列最適化コンパイラの動作を助け、バグを少なくするため、配列代入文の 上下限指定は省略しないほうがよい。
従来の .GT., .GE., .EQ., .LT., .LE., .NE. の代わりにそれぞれ >, >=, ==, <, <=, /= を使用する。数学表記に近いこの新しい構文 の方が意味がわかりやすい。
goto 文を使うとプログラムがわかりにくくなることが多い。Fortran に限
らず、ほとんどのプログラムはループと条件分岐だけで書くことができ、そ
うすることが望ましいと考えられている(構造化プログラミング)。
FORTRAN 77 では固定長のループしか表現できないため GOTO 文を用いる必
要があったが、do のあとの繰り返し指定を省略すると無限ループを表し、
ループの途中からの脱出は exit 文で行えるようになったので、構造化プロ
グラミング理論で用いられるすべてのループが GOTO 文なしで書ける。
モジュールは Fortran90 の新しい機能のうち、最も重要な機能の1つである。
しかし、これを乱用すると分かりにくいプログラムになってしまう。(例えば、
モジュールの共有変数の値は、そのモジュール外のいろいろなサブルーチンから
自由に変更可能であるが、その自由を認めると、その共有変数がどこでどういう
タイミングで変更されるのか理解が難しいプログラムになってしまう。)
そこで、プログラムを分かりやすいものにするため、モジュールの使用方法を
以下の3つの類型に限定する。
定数を参照するためのモジュール。
変数を参照するためのモジュール。common文をこれに置き換える。
パッケージを扱うためのモジュールである。(パッケージとは、ある機能を 持つサブルーチン群のこと。例えば高速フーリエ変換や、気象モデルにおけ る大気放射など。)共有変数・初期化サブルーチン・実行サブルーチン・局 所サブルーチンからなる。
いろいろなサブルーチンで使用する定数を、定数参照型モジュールにまとめ る。include と異なり、use prm, only : imax などと使用したいパラメータ だけ use できる。また、この場合、prm に imax の宣言がなければコンパイ ル時にエラーが発生してくれて、親切である。
module prm_resol ! ! 配列の大きさ(解像度)等を表す変数を設定 ! implicit none integer,parameter :: imax = 128 integer,parameter :: jmax = 64 ( 中略 ) end module prm_resol
module prm_phconst ! ! 物理定数を表す変数を設定する ! implicit none real(8),parameter :: cp = 1004.6D0 ! 比熱 real(8),parameter :: grav = 9.80665D0 ! 重力定数 real(8),parameter :: gasr = 287.04D0 ! 気体定数 ( 中略 ) end module prm_phconst
program main ! ! 温度・高度データを標準入力して、エネルギーを標準出力する ! use prm_resol , only : imax ! 使うものだけをあげよう use prm_phconst, only : cp , grav implicit none ! real(8) :: energy (imax) real(8) :: height (imax) real(8) :: tmperature (imax) integer :: i ! read (5,*) tmperature read (5,*) height do i=1,imax energy(i) = cp * tmperature(i) + grav * height(i) enddo write(6,*) energy end program main
定数参照型モジュール内の定数の値は、コンパイル時に読み込まれるので、コ ンパイラが最適化を行いやすくなる。実行時に値を変更することは、もちろんで きない。
いろいろなサブルーチンで使用する変数を、変数参照型モジュールにまとめる ことができる。
変数参照型モジュール内の共有変数の値の設定・変更は、同じモジュール内の サブルーチンでしか行ってはいけない。外部からは値を参照するのみにする。
module com_tetens implicit none real(8), save :: table(25000) ! 共有変数 ! contains ! subroutine com_tetens__ini ! 初期化サブルーチン : (table(25000)の値の設定を行う) end subroutine com_tetens__ini ! end module com_tetens
program main use com_tetens,only: & & com_tetens__ini, & ! サブルーチンcom_tetens__iniを使用。 & table ! 変数tableを使用。 : call com_tetens__ini ! tableの値の設定 : (以下、table(25000)の値を参照可能。変更はしない(ルール)。) : end program main
変数参照型モジュールの共有変数は、プログラム実行時に最初に一度だけ値を 設定し、それ以後変更しないのが理想である。値の変更がある変数については、 変数参照型モジュールではなく、引数でサブルーチンに渡す方がプログラムが分 かりやすくなる場合が多いであろう。
パッケージ型モジュール内の共有変数や局所サブルーチンにはprivate 属性を つけ、モジュール内でのみ使用し外部から直接使用しないようにする。外部との やりとりは、初期化サブルーチンと実行サブルーチンだけを通じて行う。
初期化サブルーチンで共有変数の初期値の設定を行う。
実行サブルーチンはパッケージ型モジュール内に1個もしくは複数個あり、パ ッケージ型モジュールのメインの部分である。引数を通じて外部からデータを入 力し、計算を行い、引数を通じて外部に必要なデータを出力する。
module package1 use prm_resol, only : imax,jmax implicit none private ! private属性をdefaultに public :: package1__ini, package1__run ! 外部から使用可 real(8), save :: kyouyuu(imax,jmax) ! 共有変数、外部から使用不可 ! contains ! subroutine package1__ini(a) ! 初期化サブルーチン real(8), intent(in) :: a(imax,jmax) : (kyouyuu(imax,jmax)に初期値を与える) : (データの入力は、例えば、引数から・NAMELISTから・ファイルから) : end subroutine package1__ini ! subroutine package1__run(b,c) ! 実行サブルーチン real(8), intent(in) :: b(imax,jmax) real(8), intent(out) :: c(imax,jmax) : call sub1 : (kyouyuu,bの値から、cの値を計算する。) : (kyouyuuの値の変更も可。) end subroutine package1__run ! subroutine sub1 ! 局所サブルーチン : (モジュール内部でのみ使用) end subroutine sub1 ! end module package1
パッケージ型モジュール内でのみ使用する予報変数をモジュール内の共有変数 にし、外部から隠蔽する。パッケージ型モジュール内でのみ使用するサブルーチ ンを局所サブルーチンにし、これも外部から隠蔽する。初期化サブルーチンと実 行サブルーチンのインターフェースのみを外部に公開する。
このようにして個々のモジュールの独立性を高めることにより、プログラム全 体の構造が分かりやすくなり、多人数で大規模なプログラムの共同開発を行うこ とが容易になる。
上の階層のモジュールが下の階層のモジュールをuseし使用する形になる。コン
パイル時には、下の階層のモジュールから順にコンパイルしなければいけない。
サブルーチンはモジュールに属するようにする。サブルーチンの属するモジュ
ールを use してからでないとそのサブルーチンを使用できなくなるが、コンパ
イラがコンパイル時に引数の型が一致しているかどうかチェックを行ってくれる
というメリットがある。これによりデバッグが容易になる。
サブルーチン内のワーク的変数を動的にメモリに割り付けることにより、プロ グラム作成者はワーク的変数の管理から開放され、メモリを有効利用することが できる。また実行時に配列の大きさを変更できるので、解像度に依存しないプロ グラムが作成できる。
subroutine sub( imax, jmax, a ) implicit none integer, intent(in) :: imax integer, intent(in) :: jmax real(8), intent(inout) :: a(imax,jmax) ! real(8) :: work1(imax,jmax) ! 自動配列 real(8), allocatable :: work2(:,:) ! allocate可能な配列 ! allocate(work2(imax,jmax)) ! メモリに割り付ける。 : (aの値を計算する。) : deallocate(work2) ! メモリを解放する。 end subroutine sub
上の例で、自動配列は自動的にサブルーチンの初めにメモリに割り付けられ、 サブルーチンの終わりにメモリを解放する。allocatable 配列では、allocate 文、deallocate 文で明示的に行う。
自動配列はメモリのスタック領域を使用する場合が多く、ヒープ領域を使用す る場合が多いallocatable 配列より自動配列の方が割付を高速に行えることが多 い。しかし、使用可能なスタックのサイズに制限がある場合があり、自動配列で 使用できるメモリが制限される場合がある。(OS・コンパイラによる。)
モジュールを利用すれば不要である。COMMONブロックのうち値が一定のもの はモジュール変数に書き換えよう。変数の加除を間違える心配がなくなる。 初期化サブルーチンもまとめておけるので、何が入るかすぐにわかる。コモ ンブロックのうち毎ステップ値の変わるような値の一定でないものは、引数 で渡すようにすると、どのサブルーチンでどのタイミングで値が変更される かが明瞭になる。
モジュールを利用すれば不要である。
文字数の引数の長さを誤ると予期せぬ動作がおこる。サブルーチンで長さを character(len=*) のように宣言すると誤りを防ぐことができる。>
ENTRY は複数のサブルーチンでデータを共有するために用いられてきた。共 有されるべき変数をモジュール変数とし、ENTRY で定義される入り口ごとに サブルーチンを分ければENTRY 文は不要である。
構造の不明確な INCLUDE 行の機能よりも use 文の方が同じ効果をうまくも たらす。
最適化に困難が生じる。
代わりに自由形式を使用すること。
倍精度実数型を宣言するときは real(8) を使用すること。
character(len=文字長) とすること。
data文は実行文の間におくことができるシステムも存在する。しかし、デー タ初期化は本来は宣言文に属するものであるので、実行文の間に置いてはい けない。
文関数は引用仕様が暗黙的であり、デバッグがしずらくなる。また、文関数 自体や文関数定義が後続する文関数を引用してはならないなどの制限がある。 内部関数として書いた方がよい。
特に必要がない限り、総称名を使用すること。
「IF (式) 文番号, 文番号, 文番号」の形式で、式の値の正負によってジャ
ンプする文番号が選択されたり、「GOTO (文番号, 文番号, ...) 整数式」
の形式で、式の値が n ならば n 番目の文番号にジャンプするという機能が
あった。
かわりに、ブロック if 文を使用すること。
多重の do ループを同じ文番号で終えることはできなくなる予定。また、do ループを end do 文でも continue 文でもない単純実行文で終えることはで きなくなる予定。代わりに end do を用いること。
サブルーチンの仮引数として * を列挙し、呼び出し側では * をつけた文番
号を引数として CALL すると、サブルーチン内で RETURN の後に整数型の式
(n とする)を書いた場合にサブルーチンから返るだけでなく n 番目の *
に対応するの文番号にジャンプする、という機能があった。廃止予定である
し、なにより判りにくい。「FORTRAN777で作ったオブジェクトが Fortran90
でリンクできない」といったトラブルの原因になることもある。
サブルーチンが異常な状態で終了したことを呼び出し元に伝えたいならば、
そのための引数を追加すべきである。
数字の後に H を書くと、そのあと数値の数だけの文字が文字型
ソースコードの読みやすさと編集上の理由から、タブは禁止する。
規格非合致の拡張仕様を用いると汎用性を著しく損ねてしまうので使用しな いこと。機種依存部分についてはチェックが困難であるため、著者がどうい う環境・コンパイルオプションで動作確認したのかの情報があればよい。
小文字が主流のようである。本ルールは小文字で統一した。
Fortran キーワードのみを大文字にする、という流儀もある。
書式は機種依存であるが、機能的にはほぼ同等のものが各処理系に存在す る。#include を使って切り替える、などというルールが必要か。
ここでは、一般的に禁止あるいは非推奨事項とはしないが、気象予報モデル・ データ同化プログラムのために特記すべき事項を記述する。「5.現段階では 規定しない」事項にも言及している。
変更履歴(誤字脱字の修正等は含まれません)