LoginSignup
12
7

More than 1 year has passed since last update.

Fortran FOSSプログラマ向けスタイルガイドの概要

Last updated at Posted at 2021-05-08

概要

FORD (FORtran Documenter)などを作成している,Fortran F/OSS Programmers Groupのリポジトリに,Fortran FOSSプログラマ向けのスタイルガイドラインがあったので,概要をまとめましました.
併せて著者の考えを注釈として記載しました.

言語標準への準拠

言語標準に準拠することは最重要だが,実用性の観点から下記のように妥協する.

  • 言語標準には可能な限り準拠せよ
  • ただし,実用性の観点から拡張が必要な場合は除く
  • その場合でも,複数のコンパイラによって広くサポートされている拡張を優先する

標準に準拠できない理由の例

並列計算を行う場合

HPC分野で並列計算用のハードウェアを利用するには,ライブラリなどを用いる必要がある.特化した特定の手段よりも,OpenMPやOpenACC等が好まれる1

サードパーティーライブラリを利用する場合

OSレベルの操作や実行時エラーの処理などは,非標準ライブラリを用いた混合言語プログラミングによって解決されることが多い.このような場合は,iso_c_bindigを利用して,可能な限り標準に準拠して開発することを検討せよ2

プリプロセッサディレクティブ

Fortranには標準にプリプロセッサディレクティブがないので,プリプロセッサディレクティブを使用すると,厳密には言語標準に準拠しなくなる3

プリプロセッサディレクティブは一例であるが,コードを非標準にする可能性のある他の機能にも気をつけること.

命名規則

自身の意見を盛り込んだ命名規則を採用し,それを一貫して使用する

小文字を利用する

Fortranは大文字小文字を区別しない言語であるため,構文チェックで大文字と小文字は区別できない.しかし,キャメルケースは可読性向上の一助となる可能性がある.

Fortranのキーワードとユーザが定義した名前を区別するために,Fortranキーワードを全て大文字で書く規約が採用されていたが,現在広く利用されているエディタの多くは,構文強調機能を備えているので,Fortranのキーワードを他の名前と区別できる.

  • 例外としてグローバルに置かれたパラメータがある

小文字のみを用いる場合は,スネークケースを利用する

その場合,アンダースコアの使い方を統一せよ.

  • temp_walltemp_gasという変数名を定義したなら,tempfloorfloor_tempではなくtemp_floorとせよ.

説明的で,かつ意味を持った変数名を付ける

現在のFortran(90以降)は,63文字までの長い名前が許可されている.意味のある名前を付ける検討し,名前の短縮に力を入れすぎないようにせよ.

同じ概念の処理を異なるオブジェクトに対して実行する手続きを開発する場合,手続きに意味のある説明的な名前を与えることで可読性を改善できる.

subroutine compute_transpose_of_sparse_approximate_matrix(...)

subroutine compute_transpose_of_dense_approximate_matrix(...)

subroutine compute_transpose_of_sparse_exact_matrix(...)

変数名の短縮に労力を使わない

空気の音速の定義を例にすると,いくつかの分野で使われているa4は,この暗黙の慣習に詳しくない人にとっては不明瞭である.

最悪なのは,簡潔にするためにsosという名前を採用することである.この変数は,音速の値なのか遭難信号なのか区別できない.speed_of_soundのような名前が明快で長さも適切である.

もし,複数の物質における音速が必要なら,speed_of_sound(AIR), speed_of_sound(WATER)のように,定数配列を設けて物質名をインデックスとして利用することを検討せよ5

簡潔な方が可読性向上に繋がる場合は,名前の長さを制限してもよい

音速の変数が,気体を表す構成要素である場合を仮定する6

type :: gas
  real :: gamma = 0.0
  real :: pressure = 0.0
  real :: speed_of_sound = 0.0
end type gas

associate構文を利用すると,変数名は簡単に短縮できる.

function density(low_pressure_gas)
  type(gas), intent(in) :: low_pressure_gas
  real                  :: density

  associate(gamma => low_pressure_gas%gamma,       &
            pressure => low_pressure_gas%pressure, &
            speed_of_sound => low_pressure_gas%speed_of_sound)
    density = gamma * pressure / (speed_of_sound**2)
  end associate
  return
end function density

一文字の変数名は,配列インデックスやループカウンタとして利用する場合のみ許可

1文字変数lは,数字(リテラル)の1と区別が難しいので避けるべきであるが,エディタの構文強調機能があれば区別できる.O0についても同様.

マジックナンバーを利用せず,パラメータを利用する7

論理変数には,それが伝えるべき状態を反映した名前を付ける

  • lib_initよりもlib_is_initialized
  • obj_parentよりもobj_has_parent

エンティティ8を明確化する

モジュール名,派生型名,手続き名,変数名を明確化するための規則を採用する.

下記の例のように,モジュール名と派生型名(同一スコープ内の異なるエンティティ)に同じ名前を付けたい場合がある.しかし,これは許可されていないので,モジュール名と派生型両方に意味のある名前を選択する必要がある.

module shape_sphere
  type, public :: shape_sphere ! 許可されない
  end type shape_sphere
end module shape_sphere

変数名を付ける場合も,同様の曖昧さが発生する事がある.

type(shape_sphere) :: shape_sphere ! 許可されない

接頭辞,接尾辞を利用する

モジュールに接尾辞_m,派生型に接尾辞_tを利用する.

module shape_sphere_m
  type, public :: shape_sphere_t
  end type shape_sphere_t
end module shape_sphere_m

ライブラリやパッケージを構成する全てのモジュールに,共通の接頭辞(例えばpkg_)を付ける.

module pkg_shape_sphere
  type, public :: shape_sphere
  end type shape_sphere
end module pkg_shape_sphere

異なるスタイルを利用する

キャメルケース9とスネークケースなど異なるスタイルを用いる.ただし,この方法では一つの単語からなるモジュール名(Car)と派生型名(car)は区別できない.

module shape_sphere ! モジュール名はスネークケース
  type, public :: ShapeSphere ! 派生型はキャメルケース
  end type ShapeSphere
end module shape_sphere

サブモジュールを利用する

派生型の型束縛手続きのAPI定義とその実装を分けるためにsubmoduleを利用すると,結果としてモジュール名と派生型名が区別できる.

module speaker_interface
  type :: speaker
  contains
    procedure, nopass :: speak
  end type speaker

  interface
    module subroutine speak
    end subroutine speak
  end interface
end module

submodule (speaker_interface) speaker_implementation
contains
  subroutine speak
    print "(A)", "Hello, world!"
  end subroutine speak
end submodule speaker_implementation

ここで,_interfaceはAPI定義の識別に使う接尾辞であり,接尾辞_implementationを付けたサブモジュールでの実装と区別される.派生型名は接頭辞や接尾辞がなくても十分に識別できる.

手続きを明確化する

型束縛手続きに派生型名を入れる

type :: object
  contains
    procedure :: stuff_1 => perform_stuff_1_on_object
    procedure :: stuff_2 => perform_stuff_2_on_object
    ...
end type object

キャメルケース9とスネークケースを利用して手続きを識別する

type :: gas
  real :: speed_of_sound ! 成分はスネークケース
  contains
    procedure :: ProjectOnCharacteristics ! 型束縛手続きはキャメルケース
end type gas

手続き名称には動作に関する動詞を含める

subroutineには動詞形を利用し,functionには名詞形を利用するなど,subroutinefunctionの識別に動詞の有無が利用できる.

! 動詞形
subroutine get_useful_thing
end subroutine get_useful_thing
! 名詞形
function useful_thing
end function useful_thing

ソースファイル名は,その中で定義されている主なエンティティの名前を反映する

常にimplicit none文を利用する

暗黙の型宣言は,コードの開発を加速できるが,コードの理解やデバッグを困難にする.

programmodulesubmoduleの最初にはimplicit noneを書く.外部手続き(programmodulesubmodule内で定義されていない手続き)は,最初にimplicit noneを書く.

多くのコンパイラは,暗黙の型宣言を無効化する専用のオプションを提供しているので,その利用も検討する.

複雑よりも単純な方がよい

単純さは,可読性と保守性が高いコードを作成する鍵である.

オブジェクト指向プログラミングは,より一般的で複雑なタスクに対して保守性と再利用性を向上できる10だろうが,単純なタスクに対しては複雑なだけである.

可能な限りgoto文の利用は避ける

ほとんどの場合,goto文は,構造的に安全な構文によって置き換えることができる.可読性と保守性を大幅に改善しながら性能劣化もない.しかし,非常に特殊で有用な場合は例外を認めるべきである.

gotoはそれ自体が悪ではなく,カオス的なコードを開発するようプログラマを駆り立てる可能性があることが問題である.


  1. Fortran標準として,do concurrentco-arrayが追加されている.do concurrentreductionがしたいとの要望も出されているが,OpenMPの機能をFortran標準で置き換えるのは,まだまだ先になるだろう.
    特化した特定の手段とは,恐らくOpenACCに関係しており,CUDA FortranよりもOpenACCを使えとの事だと考えられる.一方で,nvfortranでは,do concurrentを自動でGPUにオフロードする機能が追加されており,言語標準に沿った上で並列計算用ハードウェアを利用する手段が整備されつつある. 

  2. これは,例えば!DEC$ attributesのようなコンパイラ依存のディレクティブではなく,Fortran 2003のbind(name=c)を使えということであろう. 

  3. Fortran向けのプリプロセッサとしては,近年ではfyppが注目されている.原文ではCOCOというプリプロセッサが紹介されていた. 

  4. いくつかの分野ではcが使われることもある. 

  5. AIRWATERという名前から,それらがインデックスであることを想像するのは困難である.列挙型を利用することを想定していると思われるが,その場合はMaterialIndex_AIR, MaterialIndex_WATERなど接頭辞を用いた方がよりよいと考えている.あるいは,独自にmaterial_index型を定義し,その成分としてair, waterを設けた上で,material_index型定数を用いた方がよいだろう. 

  6. 著者は,gammaではなくratio_of_heat_capacitiesあるいはheat_capacity_ratioを使うべきであると考えている.associate構文で一時的かつ局所的に名前を短縮するのであれば,プログラム全体に矛盾がない限り,gamma, p, aを使ってもよいと考えている.関数gammaを利用している場合は,命名規則の検討が必要である. 

  7. 例えば定数の1を直接書かない代わりに,定数integer, parameter :: one = 1を利用しても,何も解決していない.その1が何を指しているのかを説明する変数名にせよ. 

  8. 現実世界におけるモノや概念のうち,計算機で取り扱うためにモデル化され,かつモデルが持つ情報によって一意に特定できる対象を意味する.ここでは,モジュール名,派生型名,手続き名,変数名を区別しろと言っている. 

  9. 原文はキャメルケースだが,1文字目も大文字にするのはパスカルケースのはず. 

  10. オブジェクト指向プログラミングを利用すれば自動的に保守性と再利用性が向上するのではなく,そのように設計しなければならないという制約である. 

12
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
7