コーディング · プログラミング言語
Fortran入門:初心者向けチュートリアル
結論
Fortranは、数値計算・科学技術計算のために作られた、コンパイル型の静的型付け言語です。モダンFortran(Fortran 90以降の規格)には、自由形式のソース、モジュール、動的配列、強力な配列全体の演算があり、コンパイラはそれを非常に高速な機械語にします。無料のgfortranコンパイラをインストールし、コードを.f90ファイルに保存して、gfortran hello.f90 -o helloでビルドします。
Fortranとは何か、誰が使うのか
Fortran(「formula translation」)は、1950年代にIBMでJohn Backusが率いるチームが開発した言語で、現在も現役で使われている最も古いプログラミング言語の1つです。ISOで標準化されており、主な改訂にFortran 90、95、2003、2008、2018、2023があります。古い固定形式(大文字のFORTRAN 77で、コードは7桁目から始まる)も今でもコンパイルできますが、新しいコードでは、ここで示す自由形式の構文を使います。
Fortranは、重い数値計算が重要な場面で使われています。気象・気候モデル、数値流体力学、物理や化学のシミュレーション、スーパーコンピューターで動く多くのプログラムなどです。NumPyやMATLABなどのツールの内部で使われている線形代数ライブラリBLASとLAPACKのリファレンス実装は、Fortranで書かれています。
コンパイラのインストールとhello, worldの実行
GCCの一部であるgfortranは無料で、Linux(パッケージgfortran)、macOS(Homebrewのgcc)、Windows(MSYS2またはWSL)で使えます。IntelのifxやLLVM Flangも、ほかのコンパイラです。Fortran Package Manager(fpm)を使うと、プロジェクトの作成とビルドができます。
program hello
implicit none
print *, "Hello, world!"
end program hellogfortran hello.f90 -o hello
./hello
# Hello, world!print *はリスト指向出力を使い、間隔は自動で決まります(先頭に空白が入る点に注意)。Fortranは大文字・小文字を区別しません。PRINT、Print、printは同じです。
変数と基本の型
宣言はすべて、プログラム単位の先頭、実行文の前に置きます。必ずimplicit noneを書いてください。書かないと、宣言されていないIからNで始まる名前は黙って整数になり、それ以外はすべて実数になります。
| 型 | 宣言の例 | 補足 |
|---|---|---|
| integer | integer :: count = 3 | 通常は32ビット。64ビットにはinteger(int64)を使います |
| real | real :: x | 通常は単精度 |
| real(real64) | real(real64) :: y | 倍精度。科学計算では通常こちらを選びます |
| complex | complex :: z = (1.0, 2.0) | 組み込みの複素数 |
| logical | logical :: done = .false. | .true. / .false.。演算子は.and.、.or.、.not. |
| character | character(len=20) :: name | 固定長の文字列。空白で埋められます |
program types
use, intrinsic :: iso_fortran_env, only: int64, real64
implicit none
integer :: count = 3
integer(int64) :: big = 9000000000_int64
real(real64) :: third
logical :: done = .false.
character(len=:), allocatable :: name
third = 1.0_real64 / 3.0_real64
name = "Ana"
print *, count, big, third, done, name
end program types接尾辞_real64は、リテラルの種別(精度)を指定します。付けないと、1.0は単精度の定数となり、倍精度の変数に代入するときに桁が失われます。
制御構文
program control
implicit none
integer :: i, total, score
total = 0
do i = 1, 10
if (mod(i, 2) == 0) cycle ! skip even numbers
total = total + i
end do
print *, "sum of odd numbers:", total ! 25
score = 85
if (score >= 90) then
print *, "A"
else if (score >= 80) then
print *, "B"
else
print *, "C"
end if
select case (score)
case (90:)
print *, "excellent"
case (80:89)
print *, "good"
case default
print *, "keep going"
end select
end program controldo i = 1, 10は両端を含み、省略可能な3つ目の値で刻み幅を指定します(do i = 10, 1, -1)。cycleは次の繰り返しに進み、exitはループを抜けます。do while (condition)や、exitを使う単純なdoも使えます。コメントは!で始まります。
関数・サブルーチン・モジュール
Fortranの手続きには2種類あります。関数は値を返し、式の中で使います。サブルーチンはcallで呼び出し、結果を引数を通して返します。手続きはモジュールに入れると、コンパイラがすべての呼び出しをチェックできます。
module geometry
implicit none
contains
function circle_area(r) result(a)
real, intent(in) :: r
real :: a
a = 3.14159 * r**2
end function circle_area
subroutine swap(a, b)
integer, intent(inout) :: a, b
integer :: tmp
tmp = a
a = b
b = tmp
end subroutine swap
end module geometry
program demo
use geometry
implicit none
integer :: x, y
x = 1
y = 2
print *, circle_area(2.0) ! about 12.566
call swap(x, y)
print *, x, y ! 2 1
end program demo引数は参照渡しなので、手続きは呼び出し側の変数を変更できます。intent(in)、intent(out)、intent(inout)は、各引数に許される操作を文書化し、強制します。**はべき乗の演算子です。
配列:Fortranの特徴的な機能
配列はFortranの中心です。添字はデフォルトで1から始まり(任意の下限を指定できます)、ループなしで配列全体に算術演算ができ、スライスはa(start:end)で書き、多くの組み込み関数が配列を直接扱います。
program arrays
implicit none
integer :: i
real :: a(5), b(5)
real, allocatable :: c(:)
integer :: m(2, 3)
a = [(real(i), i = 1, 5)] ! 1.0 2.0 3.0 4.0 5.0
b = 2.0 * a + 1.0 ! whole-array arithmetic
print *, b(2:4) ! 5.0 7.0 9.0
print *, sum(a), maxval(b) ! 15.0 11.0
allocate(c(10))
c = 0.0
print *, size(c) ! 10
deallocate(c)
m = reshape([1, 2, 3, 4, 5, 6], [2, 3])
print *, m(2, 1) ! 2 (column-major order)
end program arrays多次元配列は列優先で格納されます。メモリ上では最初の添字が最も速く変化し、Cとは逆です。高速化のためには、最も内側のループが最初の添字を回るようにしてください。ほかにもmatmul、dot_product、transpose、minval、countなどの組み込み関数が便利です。モダンFortranには、並列プログラム向けのdo concurrentループとコアレイもあります。
エラー処理
Fortranに例外はありません。失敗しうる文は、ステータス引数を受け取ります。入出力にはiostat、allocateにはstatです。値を確認して対処してください。省略するとエラーでプログラムが止まります。error stopは、エラーでプログラムを終了させます。
program parse
implicit none
character(len=20) :: text
integer :: n, ios
text = "abc"
read(text, *, iostat=ios) n
if (ios /= 0) then
print *, "not a number: ", trim(text)
else
print *, "n =", n
end if
end program parse上のように文字型変数から読み込むことを内部読み込みといい、文字列を数値に変換する一般的な方法です。
小さな完成プログラム:平均と標準偏差
module stats
implicit none
integer, parameter :: dp = kind(1.0d0)
contains
pure function mean(x) result(m)
real(dp), intent(in) :: x(:)
real(dp) :: m
m = sum(x) / size(x)
end function mean
pure function std_dev(x) result(s)
real(dp), intent(in) :: x(:)
real(dp) :: s
s = sqrt(sum((x - mean(x))**2) / (size(x) - 1))
end function std_dev
end module stats
program main
use stats
implicit none
real(dp) :: values(5)
values = [2.0_dp, 4.0_dp, 4.0_dp, 5.0_dp, 7.0_dp]
print '(a, f6.3)', "mean = ", mean(values)
print '(a, f6.3)', "std dev = ", std_dev(values)
end program main
! mean = 4.400
! std dev = 1.817x(:)の宣言は形状引き継ぎ配列なので、関数は任意の長さの配列を受け取れます。pureは、関数に副作用がないことを約束します。書式'(a, f6.3)'は、文字列に続けて、幅6文字・小数3桁の実数を出力します。
次に学ぶこと
- fortran-lang.org:クイックスタートのチュートリアルとベストプラクティスがあるLearnセクション。
- コンパイラのオプションと組み込み手続きについてのGNU Fortran(gfortran)のマニュアル。
- Fortran Package Manager(fpm)でプロジェクトを作り、Fortran標準ライブラリ(stdlib)などのライブラリを使ってみる。
- 学習中は、
gfortran -Wall -fcheck=allのように、警告と実行時チェックを付けてコンパイルする。
よくある質問
Fortranは今でも使われていますか?
はい。Fortranは科学技術計算やハイパフォーマンスコンピューティング、特に気候、気象、物理、工学のシミュレーションで広く使われ続けており、言語の規格も更新され続けています。
.fファイルと.f90ファイルの違いは何ですか?
慣例として、.fと.forのファイルはFORTRAN 77の古い固定形式のレイアウトを使い、桁位置が重要です。.f90のファイルは自由形式のソースです。.f90の拡張子は、Fortran 90に限らず、あらゆる現代の規格の自由形式コードに使われます。
Fortranはなぜ速いのですか?
この言語は数値計算のために設計されています。配列が組み込まれ、エイリアシングの規則によってコンパイラが積極的に最適化でき、数十年にわたるコンパイラの開発がFortranを対象にしてきました。よく書かれたCやC++でも、同程度の速度に達します。
Fortranの配列は0から始まりますか、1から始まりますか?
デフォルトでは1から始まりますが、下限は自由に選べます。たとえばreal :: a(0:9)は、添字0から9までの10要素を宣言します。