標準コーディングルール
2001年9月1日版
目次
1.はじめに
2.スタイルルール(推奨リスト)
3.Fortran90
の機能を生かして
4.禁止事項
5.現段階では規定していないが重要なもの
付録 気象予報モデル・データ同化プログラムのためのコメント
参考文献・参考URL
変更履歴
ヨーロッパや米国の気象機関では、プログラムのソースコードを理解しやすく管理しやすいものにし、またソースコードの交換を容易にするため、プログラミングルールが作成されている。日本でもこれらを参考にして、気象庁数値予報課と気象研究所気候研究部・予報研究部が中心となって日本版標準コーディングルールを作成している。その目的は、プログラミング・スタイルを統一することにより、可読性と移植性を高め、維持管理を容易にすることである。この目的に賛同する開発担当者は、このルールに従いプログラムを作成し、同時にこのルールを改善するよう努力してほしい。(本ルールを守らなければ気象庁の業務プログラムとして登録されない、などということはありません。)
このプログラミングルールではFortran 90 の使用を前提としている。Fortran 90 は、 FORTRAN 77 と互換性を保ちながら様々な機能を付加したもので、従来と比較して多くの点で改良されている。最新の JIS 規格は Fortran 95 であるが、Fortran 90 しか使えない環境が依然多い状況であると思われるので、Fortran 95 の新機能は当面使わないことを推奨する。
対象とするプログラムとして、気象予報モデルとデータ同化モデルを想定してはいるが、高速で実行する必要がある大規模プログラムを多人数で共同開発する場合には、異分野でも役に立つと考えている。
一部のルールについては断定的に記述していない部分がある。今後 Fortran 90 についてより多くの経験をつむことにより、今後の改訂版では追加・削除・変更されることを了解していただきたい。
7-72桁に文を書かなければならない等の制約がなくなり、それ以外の桁位置に書いた文字が無視されるなどの誤りを防ぐことができる。
プログラム単位とは、主プログラム、外部関数、外部サブルーチン、モジュール、BLOCK
DATA のいずれかである。たとえば、外部サブルーチン
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 文なしで書ける。
3.1 モジュールの使い方
3.1.1 モジュールの3つの使用方法
モジュールは
Fortran90の新しい機能のうち、最も重要な機能の1つである。しかし、これを乱用すると分かりにくいプログラムになってしまう。(例えば、モジュールの共有変数の値は、そのモジュール外のいろいろなサブルーチンから自由に変更可能であるが、その自由を認めると、その共有変数がどこでどういうタイミングで変更されるのか理解が難しいプログラムになってしまう。)
そこで、プログラムを分かりやすいものにするため、モジュールの使用方法を以下の3つの類型に限定する。
定数を参照するためのモジュール。
変数を参照するためのモジュール。common文をこれに置き換える。
パッケージを扱うためのモジュールである。(パッケージとは、ある機能を持つサブルーチン群のこと。例えば高速フーリエ変換や、気象モデルにおける大気放射など。)共有変数・初期化サブルーチン・実行サブルーチン・局所サブルーチンからなる。
3.1.2 定数参照型モジュール
いろいろなサブルーチンで使用する定数を、定数参照型モジュールにまとめる。include と異なり、use prm, only : imax などと使用したいパラメータだけ use できる。また、この場合、prm にimax の宣言がなければコンパイル時にエラーが発生してくれて、親切である。
定数参照型モジュールの例1 (prm_resol.F90)
module prm_resol ! ! 配列の大きさ(解像度)等を表す変数を設定 ! implicit none integer,parameter :: imax = 128 integer,parameter :: jmax = 64 ( 中略 ) end module prm_resol定数参照型モジュールの例2 (prm_phconst.F90)
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上記定数参照型モジュールを参照等をするメインプログラムの例 (main.F90)
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
定数参照型モジュール内の定数の値は、コンパイル時に読み込まれるので、コンパイラが最適化を行いやすくなる。実行時に値を変更することは、もちろんできない。
3.1.3 変数参照型モジュール
いろいろなサブルーチンで使用する変数を、変数参照型モジュールにまとめることができる。
変数参照型モジュール内の共有変数の値の設定・変更は、同じモジュール内のサブルーチンでしか行ってはいけない。外部からは値を参照するのみにする。
変数参照型モジュールの例 (com_tetens.F90)
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上記変数参照型モジュールを参照等をするメインプログラムの例 (main.F90)
program main use com_tetens,only: & & com_tetens__ini, & ! サブルーチンcom_tetens__iniを使用。 & table ! 変数tableを使用。 : call com_tetens__ini ! tableの値の設定 : (以下、table(25000)の値を参照可能。変更はしない(ルール)。) : end program main
変数参照型モジュールの共有変数は、プログラム実行時に最初に一度だけ値を設定し、それ以後変更しないのが理想である。値の変更がある変数については、変数参照型モジュールではなく、引数でサブルーチンに渡す方がプログラムが分かりやすくなる場合が多いであろう。
3.1.4 パッケージ型モジュール
パッケージ型モジュール内の共有変数や局所サブルーチンには private 属性をつけ、モジュール内でのみ使用し外部から直接使用しないようにする。外部とのやりとりは、初期化サブルーチンと実行サブルーチンだけを通じて行う。
初期化サブルーチンで共有変数の初期値の設定を行う。
実行サブルーチンはパッケージ型モジュール内に1個もしくは複数個あり、パッケージ型モジュールのメインの部分である。引数を通じて外部からデータを入力し、計算を行い、引数を通じて外部に必要なデータを出力する。
パッケージ型モジュールの例 (package1.F90)
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
パッケージ型モジュール内でのみ使用する予報変数をモジュール内の共有変数にし、外部から隠蔽する。パッケージ型モジュール内でのみ使用するサブルーチンを局所サブルーチンにし、これも外部から隠蔽する。初期化サブルーチンと実行サブルーチンのインターフェースのみを外部に公開する。
このようにして個々のモジュールの独立性を高めることにより、プログラム全体の構造が分かりやすくなり、多人数で大規模なプログラムの共同開発を行うことが容易になる。
3.1.5 モジュールの階層構造
上の階層のモジュールが下の階層のモジュールをuseし使用する形になる。コンパイル時には、下の階層のモジュールから順にコンパイルしなければいけない。
サブルーチンはモジュールに属するようにする。サブルーチンの属するモジュールを
use してからでないとそのサブルーチンを使用できなくなるが、コンパイラがコンパイル時に引数の型が一致しているかどうかチェックを行ってくれるというメリットがある。これによりデバッグが容易になる。
3.2 動的割り付け
サブルーチン内のワーク的変数を動的にメモリに割り付けることにより、プログラム作成者はワーク的変数の管理から開放され、メモリを有効利用することができる。また実行時に配列の大きさを変更できるので、解像度に依存しないプログラムが作成できる。
動的割り付けサブルーチンの例 (sub.F90)
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・コンパイラによる。)
4.1 記憶列結合
モジュールを利用すれば不要である。COMMONブロックのうち値が一定のものはモジュール変数に書き換えよう。変数の加除を間違える心配がなくなる。初期化サブルーチンもまとめておけるので、何が入るかすぐにわかる。コモンブロックのうち毎ステップ値の変わるような値の一定でないものは、引数で渡すようにすると、どのサブルーチンでどのタイミングで値が変更されるかが明瞭になる。
モジュールを利用すれば不要である。
Fortran 90 では、記憶列結合以外では、実引数と仮引数の形状の一致を要求している。従来は、実際には n次元配列をサブルーチンに渡し、サブルーチン内では1次元配列として扱うことは慣習としてよく行われてきた。このような引き渡し方は今後機能しない可能性も大きい。文字数の引数の長さを誤ると予期せぬ動作がおこる。サブルーチンで長さを character(len=*) のように宣言すると誤りを防ぐことができる。
ENTRY は複数のサブルーチンでデータを共有するために用いられてきた。共有されるべき変数をモジュール変数とし、ENTRY で定義される入り口ごとにサブルーチンを分ければENTRY 文は不要である。
4.2 新機能のうち冗長なもの
構造の不明確な INCLUDE 行の機能よりも use 文の方が同じ効果をうまくもたらす。
最適化に困難が生じる。
4.3 冗長な旧機能
代わりに自由形式を使用すること。
倍精度実数型を宣言するときは real(8) を使用すること。
character(len=文字長) とすること。
data文は実行文の間におくことができるシステムも存在する。しかし、データ初期化は本来は宣言文に属するものであるので、実行文の間に置いてはいけない。
文関数は引用仕様が暗黙的であり、デバッグがしずらくなる。また、文関数自体や文関数定義が後続する文関数を引用してはならないなどの制限がある。内部関数として書いた方がよい。
特に必要がない限り、総称名を使用すること。
4.4 廃止予定事項
「IF (式) 文番号,
文番号, 文番号」の形式で、式の値の正負によってジャンプする文番号が選択されたり、「GOTO
(文番号, 文番号, ...) 整数式」の形式で、式の値が
n ならば n
番目の文番号にジャンプするという機能があった。
かわりに、ブロック if
文を使用すること。
多重の do ループを同じ文番号で終えることはできなくなる予定。また、do ループを end do 文でも continue 文でもない単純実行文で終えることはできなくなる予定。代わりに end do を用いること。
サブルーチンの仮引数として
* を列挙し、呼び出し側では *
をつけた文番号を引数として CALL
すると、サブルーチン内で RETURN
の後に整数型の式(n
とする)を書いた場合にサブルーチンから返るだけでなく
n 番目の * に対応するの文番号にジャンプする、という機能があった。廃止予定であるし、なにより判りにくい。「f77
で作ったオブジェクトが f90 でリンクできない」といったトラブルの原因になることもある。
サブルーチンが異常な状態で終了したことを呼び出し元に伝えたいならば、そのための引数を追加すべきである。
数字の後に H を書くと、そのあと数値の数だけの文字が文字型
4.5 その他
ソースコードの読みやすさと編集上の理由から、タブは禁止する。
規格非合致の拡張仕様を用いると汎用性を著しく損ねてしまうので使用しないこと。機種依存部分についてはチェックが困難であるため、著者がどういう環境・コンパイルオプションで動作確認したのかの情報があればよい。
小文字が主流のようである。本ルールは小文字で統一した。
Fortran キーワードのみを大文字にする、という流儀もある。
書式は機種依存であるが、機能的にはほぼ同等のものが各処理系に存在する。#include を使って切り替える、などというルールが必要か。
ここでは、一般的に禁止あるいは非推奨事項とはしないが、気象予報モデル・データ同化プログラムのために特記すべき事項を記述する。「5.現段階では規定しない」事項にも言及している。
参考URL
変更履歴(誤字脱字の修正等は含まれません)
<本コーディングルール作成に携わった人々>
室井ちあし 気象研究所予報研究部(←気象庁予報部数値予報課)
豊田英司 気象庁予報部数値予報課
吉村裕正 気象研究所気候研究部
保坂征宏 気象研究所気候研究部
杉正人 気象研究所気候研究部
本ルールについて、気象庁内外の多くの方からコメントをいただいています。名前はここには記しませんが、関心をもっていただいている皆さんに感謝しています。