fortran 基礎文法最速マスター
基礎文法最速シリーズに便乗して*1書きかけですが上げます。fortranは書き方がフリーダム過ぎてまとめきれません。「基礎文法」なので「fortranで線形代数の計算の雛形が書ける」くらいの内容を目標にして書いてみました。説明した事項は「自力で書ける」「レガシーコードが少しは読める」くらいの内容を含んでいるつもりではあるのですが…。
fortranのインストールと簡単なプログラム作成
[2016.1.1]64bit の Windows10 上でフリーの fortran コンパイラを導入して、簡単なプログラムを作成する - あらきけいすけの雑記帳 Win7, 8でもOK
64bit の Windows 7 上でフリーの fortran コンパイラを導入して、簡単なプログラムを作成する - あらきけいすけの雑記帳
Windows上でフリーのfortranコンパイラを導入して、簡単なプログラムを作成する - あらきけいすけの雑記帳
参考にした文献
Compaq Visual Fortran 6.6 のドキュメント(ボクは手許に製本された製品を持っている)
ホームページ http://www.xlsoft.com/jp/products/intel/cvf/docs/ の中の
言語リファレンス http://www.xlsoft.com/jp/products/intel/cvf/docs/vf-html/lr/lr00.htm
多分、これが日本語で容易に無料で参照できる唯一の fortran の網羅的な文法書でしょう。無料で参照できるのはとてもありがたいことです。*2
fortran 95 の宣伝
- fortran 95 では関数を再帰的に呼び出せます。(でもこのまとめに出ません。ごめんなさい)
- fortran 95 ではポインタが一応あるので、連結リストが作れます。(でもこのまとめに出ません。ごめんなさい)*3
- fortran 95 の MODULE でオブジェクト指向プログラミングができます。(でもこのまとめに出ません。ごめんなさい)
- fortran 95 では演算子のオーバーロードができます。(でもこのまとめに出ません。ごめんなさい)
- fortran 95 では fortran のレガシーなコードも走ります。(このせいで fortran のソースコードの書き方がフリーダム過ぎになります。コンパイラ作る人は本当にえらいと思います。)
勉強のツボ
これはボクの個人的な偏見なのですが、新しい手続き型プログラミング言語を勉強するときは次の4個の項目を意識して勉強するといいと思います。
この文書では
コードを書くときに利用する文字
- 半角文字を使え
- 大文字、小文字の区別が無い[レガシー, fortran 95 共通]
REAL(8)::A,B,C
なんて書き方ができる。
a=b+c - 空白文字:変数名の中にすきまがあってもOK[ただし固定形式のみ]
INTEGER ABC
これを使うことは無いとは思いますが、レガシーでかなり異様な仕様だと思うのでメモ
A B C = 5
ソースコードの書き方
- 自由形式[fortran 95]
- 半角文字で1行132文字まで
- それを越えたら継続行。行末にアンパサンド(&)を書くと次の行の1文字目から前行の文の続き。(詳しくは下を見てね)
ファイル名は *.f90 にする(使っているコンパイラのマニュアル参照してね)
- 半角文字で1行132文字まで
- 固定形式[レガシー, fortran 95]
レガシーな書き方。fortran関連のマニュアルに掲載されているサンプルコードは大抵こっち。
- 蛇足
- 自由形式と固定形式を同じファイルに書くことはできない。けど
gfortran hoge1.f90 hoge2.f
のように別々に保存して1個の実行ファイル(executable)を作ることはできる。
- レガシーな書き方のサンプルコードでは制御構造にインデントを使う習慣が無い…場合が結構ある。
- 自由形式と固定形式を同じファイルに書くことはできない。けど
四則演算
文(ひとつひとつの命令)
- 1行に1文が原則
- 長い文は継続行で延長
- 自由形式の場合:次に続けたい行の行末に&(アンパサンド)を書く
b(i,j)= a(i-1,j-1) - a(i,j-1) - a(i+1,j-1) &
39行続けられる。
- a(i-1,j ) + a(i,j)*8 - a(i+1,j ) &
- a(i-1,j+1) + a(i,j+1) - a(i+1,j+1)
- 固定形式の場合:続きの行の6文字目にゼロか空白でない何かを書く
b(i,j)= a(i-1,j-1) - a(i,j-1) - a(i+1,j-1)
fortran 95 の固定形式では19行続けられる。
& - a(i-1,j ) + a(i,j)*8 - a(i+1,j )
& - a(i-1,j+1) + a(i,j+1) - a(i+1,j+1)
- 自由形式の場合:次に続けたい行の行末に&(アンパサンド)を書く
変数(データのいれもの):型、宣言
- 名前の長さ:最長31文字[fortran 95]
[蛇足]レガシーな書き方のコードではしばしば手続名、変数名を6文字以内に押さえ込む。 - 暗黙の型宣言
これは蛇蝎のごとく嫌われているデフォルトの仕様
- 暗黙の型宣言がある
fortranのコードは変数宣言を書かなければ, a,b,..,h,o,p,...,z で始まる変数は自動的に REAL 型(単精度実数型, Cの float に相当), i, j, k, l, m, n は INTEGER 型と決まっている。これは関数プログラムの戻り値にもあてはまる。
fortranのマニュアルでソースの一部が示されているとき、その中の変数の型は暗黙の型宣言に従っていると想定して読まなくてはならない場合がある。
- 暗黙の型宣言を作れる
IMPLICIT 文というおちゃめな命令文がある。例えばIMPLICIT DOUBLE PRECISION (a-h,o-z)
をルーチンの冒頭に書くと a,b,..,h,o,p,...,z で始まる変数は自動的に DOUBLE PRECISION 型(倍精度実数型, C の double に相当)になる。
- 暗黙の型宣言を殺せる
IMPLICIT NONE
と書いておくと暗黙の型宣言を利用できなくなります。これを書いたら利用する変数はすべて宣言しないといけません。
- 暗黙の型宣言を無視できる
IMPLICITを生かしたままでも変数宣言をすればそちらが優先。
- 暗黙の型宣言がある
- fortranでよく利用する型[fortran 95]
丸括弧の中の N には変数1個のバイト長(4とか8とかサポートされている数値)を書く。倍精度の複素数は COMPLEX(8)型名 内容 INTEGER(N) 整数型 REAL(N) 実数型 COMPLEX(N) 複素数型, REAL(N)×2 - fortranでよく利用する型[レガシー]
型名 fortran 95 内容 INTEGER INTEGER(4) 整数型, C の int REAL REAL(4) 単精度実数型, C の float DOUBLE PRECISION REAL(8) 倍精度実数型, C の double COMPLEX COMPLEX(4) 複素数型, 単精度実数型×2 - 変数の宣言[fortran 95]
型名のあとにコロンを2個を入れて変数名を書くREAL(8)::A,B,C
- 変数の宣言[レガシー]
型名のあとに空白を入れて変数名を書くDOUBLE PRECISION A,B,C
変数(データの入れ物):配列
- DIMENSIONを使う
- [レガシー]変数の名前だけ先に宣言して、配列長をDIMENSION文で決める。
DOUBLE PRECISION A
DIMENSION A(3,4,5) - [fortran 95]変数の型宣言の限定子(DIMENSION属性)として使う
REAL(8),DIMENSION(3,4,5)::A
- [レガシー]変数の名前だけ先に宣言して、配列長をDIMENSION文で決める。
- 変数名の後に配列長を書く[レガシー?]
DOUBLE PRECISION A(3,4,5)
- 配列の添え字はデフォルトでは1から始まる
REAL(8),DIMENSION(3)::A ならば A(1), A(2), A(3) - 配列の添え字の範囲を自分で決めることができる
添え字の範囲の書き方は [開始]:[終了] (コロン1個が区切り子)
REAL(8),DIMENSION(0:3)::A ならば A(0), A(1), A(2), A(3)
プログラム
手続き型のプログラミング言語ではプログラム構成単位の書式は
プログラム構成単位のインターフェースになっています。例えばCでは
プログラムの本体
int main (void) プログラム構成単位のインターフェースです。Cではこの書式は「関数」として統一されていますが、fortranではいくつかのカテゴリに分かれています。
{ printf("hello"); } プログラムの本体(いわゆる「複文」ですね)
主プログラム
プログラムの実行で一番最初に動くもの(program startup, エントリポイント)です。
- 正式な書式
[PROGRAM 名前]
[ ] の部分は無くてもいい部分。
[宣言部]
[実行部]
[CONTAINS
内部手続部]
END [PROGRAM [名前] ] - サンプル:Hello world コード
PROGRAM HELLO_WORLD
WRITE(*,*)"Hello, world!"
END PROGRAM HELLO_WORLD - サンプル:最小限の Hello world コード
WRITE(*,*)"Hello, world!"
END - サンプル:ムダにCONTAINSを使った Hello world コード
PROGRAM HELLO_WORLD
CALL HELLO
CONTAINS
SUBROUTINE HELLO
WRITE(*,*)"Hello, world!"
END SUBROUTINE HELLO
END PROGRAM HELLO_WORLD
関数
計算をやらせるための命令パッケージ
- 正式な書式
[prefix] FUNCTION 名前 ([仮引数リスト]) [RESULT (名前)]
[ ] の部分は無くてもいい部分。
[宣言部]
[実行部]
END [FUNCTION [名前] ] - 定義と呼出のサンプルコード
FUNCTION FADD(X,Y) ! 関数の定義のサンプル
REAL(8)::X,Y,FADD ! 関数名も型宣言は必要
FADD=X+Y ! デフォルトでは関数名が戻り値の変数名である
END
PROGRAM HELLO_WORLD ! 関数の呼出のサンプル
REAL(8)::A,B,C,FADD ! 呼出側でも戻り値の型宣言が必要
A=1
B=1
C=FADD(A,B) ! 関数の呼出しは関数名と実引数
WRITE(*,*)A,B,C
END - サンプルコード2(呼出側のPROGRAMは上と同じもので動く)
REAL(8) FUNCTION FADD(X,Y) RESULT(Z)
! 結果を変数 Z に代入する書き方
! これでも呼出側には REAL(8)::FADD が必要
REAL(8)::X,Y
Z=X+Y
END FUNCTION FADD [使用上の重要な注意] fortran の引数はいわゆる「参照渡し(変数のアドレスの値を関数に与えること)」なので、「子」の側で変数の書き換えができてしまいます。
FUNCTION FADD(X,Y)
これを防衛するために fortran 95 には宣言文にINTENT属性を書くという仕様が備わっています
REAL(8)::X,Y,FADD
X=X+1 ! 書き換えをやってしまっている
FADD=X+Y
ENDFUNCTION FADD(X,Y)
REAL(8),INTENT(IN)::X,Y ! これを書くとコンパイラがエラーを出してくれる
REAL(8)::FADD
X=X+1 ! 書き換えをやってしまっている
FADD=X+Y
END
サブルーチン
何かをやらせるための命令のパッケージ
- 正式な書式
[prefix] SUBROUTINE 名前 [([仮引数リスト])]
[ ] の部分は無くてもいい部分。
[宣言部]
[実行部]
END [SUBROUTINE [名前] ] - 呼出はCALL文を用いる。引数が無い場合には実引数を囲む ( ) は書かなくてもよい。
- サンプルコード。
SUBROUTINE SADD (X,Y,Z) ! サブルーチンの定義のサンプル
fortran の引数はいわゆる「参照渡し」です。このコードでは変数Cの書き換えが起こっています。そしてサブルーチンとはそういう使い方をするものです(きりっ。[fortran 95]潰したくない引数には宣言文に INTENT(IN) 属性を書き加えます。
REAL(8)::X,Y,Z
Z=X+Y
END
PROGRAM HELLO_WORLD ! サブルーチンの呼出のサンプル
REAL(8)::A,B,C
A=1
B=1
CALL SADD(A,B,C) ! サブルーチンを呼出し
WRITE(*,*)A,B,C
END - 引数を持たない場合
SUBROUTINE HELLO ! サブルーチンの定義のサンプル
WRITE(*,*)"hello, world!"
END
PROGRAM HELLO_WORLD ! サブルーチンの呼出のサンプル
CALL HELLO ! サブルーチンを呼出し
END
制御構造
条件による分岐には IF...THEN 文を使いますIF (condition) THEN
[TODO]
END IF
条件判定のための論理式の主なものです。
fortran 95 レガシー 内容 a == b A .EQ. B 等しい a /= b A .NE. B 等しくない a < b A .LT. B 小なり a <= b A .LE. B 等しいまたは小なり a > b A .GT. B 大なり a >= b A .GE. B 等しいまたは大なり A .AND. B AかつB 論理積 A .OR. B AまたはB 論理和 .NOT. B Bの否定(単項演算) - インクリメントによる反復にはDO文を使います。
[fotran 95]DO 文で始まるブロックの終了には END DO 文を使います。
でもC言語のfor文のようなアナーキーなことはできませんfortran 95 Cのfor文との比較 DO I=1,10
[TODO]
END DOfor(i=1;i<=10;i++){
[TODO]
}int a,b; // これはC文法的に許されるよ
for ( a=0, b=0; a<10; printf("a=%d b=%d\n",a,b+=++a) ); - [fortran 95]インクリメントの変数は整数型です。
インクリメントの間隔のデフォルト値は1です。インクリメント幅の値は指定することができます。DO I=1,10,2 ! この例では1,3,5,7,9と変化します
[TODO]
END DO - DOブロックのネスト(入れ子)ができます。ネストはちゃんと対応する END DO 文を書かねばなりません。
DO J=1,10
DO I=1,10
A(I,J)=I+10*J
END DO
END DO - [レガシー]DOブロックの終了を文番号で指定することもできます。
DO 10 I=1,10
ブロックの終了にはCONTINUE文を使うことが多いのですが、普通の文を指定することもできます(古い教科書のサンプルコードで見かけます)
A=A+I
10 CONTINUEDO 10 I=1,10
ネストしたDOブロックの終了を一つの行で済ませることもあります。
10 A=A+IDO 10 J=1,10
文番号でブロックを指定するときはきちんとネストさせないといけません
DO 10 I=1,10
A=A+I+10*J
10 CONTINUEDO 10 J=1,10
DO 20 I=1,10
A=A+I+10*J
10 CONTINUE ! これは文法的にダメ
20 CONTINUE ! "10"行と"20"行が逆 - 条件による反復 while も DO 文で書きます
DO WHILE (condition)
[TODO]
END DO - サンプルコード: IF...THEN...ELSE IF...ELSE...END IF, DO WHILE (...)...END DO のサンプルです。実際にきちんと走るバビロニア式アルゴリズムで平方根を計算するFUNCTIONです。
REAL(8) FUNCTION babylonianSQRT(x) RESULT(z)
! バビロニア式アルゴリズムによる平方根の計算(負の値が代入されると-1を返す)
REAL(8),INTENT(IN)::x
REAL(8),PARAMETER::EPS=1.D-16
REAL(8)::y,yp,er
IF ( x < 0 ) THEN
z= -1;
ELSE IF ( x == 0 ) THEN
z= 0;
ELSE
y= 1.D+0
er= EPS*2
DO WHILE ( er > EPS )
yp= y
y= .5D+0*(y+x/y)
er= ABS(y-yp)/y
END DO
z=y
END IF
END
ファイル入出力の最低限の知識
まずは最小限のサンプルコード。
- 倍精度実数1個が入ったデータファイルを作成する例
まずはテキストファイルに出力する例REAL(8)::A=1.0
これを実行するとfort.1というファイル名のファイルに(もし無ければそれを勝手に作って)テキスト形式で書き出します(ファイル名はコンパイラのマニュアル見てね。それからファイル作成の権限がないとコケます、多分)。ファイル名を指定したい場合は OPEN 文, CLOSE 文を書きます
WRITE (1,*) A
! | |
! | 書式[必須]: * は「デフォルトの書式」の意味
! 装置番号[必須]: 5(標準入力),6(標準出力)ではない正の整数値OPEN(1,FILE="test.txt")
次にバイナリファイル(書式なし順番WRITE文と言います)として作成する例
! | |
! | ファイル名[必須]
! 装置番号[必須]
この間のどこかにWRITE文が入る
CLOSE(1)
! |
! 装置番号[必須]REAL(8)::A=1.0
これも fort.1 を勝手に作成します。さてちょっとビックリする話ですが、出来上がったファイルはサイズが16バイトです。というのも
WRITE (1) A
! |
! 装置番号[必須]: 5(標準入力),6(標準出力)ではない正の整数値WRITE文が書き出すデータは [4バイトの記録長データ][データ本体][4バイトの
ファイル名を指定するときのOPEN文の最小限の書式です何か記録長データ] なので「書き込んだバイナリデータのサイズ+8バイト×WRITE文の数」のファイルができます。OPEN(1,FILE='test.dat',FORM='UNFORMATTED')
! | | |
! | | 書式指定子[必須]
! | ファイル名[必須]
! 装置番号[必須]
[WRITE文が続く]
[CLOSE(1)文が続く] - 倍精度実数1個が入ったファイルを読む例:
REAL(8)::A
ファイル名が fort.1 の場合の最小限のコードは
OPEN(1,file="test.txt") ! テキストファイルを開く
READ(1,*)A ! テキストで書かれたデータを読んで A に格納
CLOSE(1)REAL(8)::A
テキストファイルの読み込みではREAD文の2個目の引数の * (アスタリスク)は必須です。
READ(1,*)A
バイナリファイルで倍精度実数1個を読み込む場合の最小限はREAL(8)::A
READ文に2個目の引数が無いときは「バイナリのデータを前から順番に」読みに行きます。ファイル名が fort.1 の場合の最小限のコードは
OPEN(1,file="test.dat",FORM='UNFORMATTED') ! バイナリファイルを開くとき
READ(1)A
CLOSE(1)REAL(8)::A
READ(1)A
ごめん、書きかけ
変更履歴
2010.2.8: 最小限の WRITE, READ を書く
2010.2.4: ファイルI/Oを書き始めてみた。他にもちょこっと書き加え。
2010.2.3: とにかく上げてみた
*1:(基礎|変態)文法最速マスターシリーズのまとめ - gifnksmの雑多なメモ
*2:これは個人的な感想ですが、書店等で比較的容易に入手できる fortran 本は『fortran入門』タイプの本ばかりで、まとまった「ルールブック」的な書籍(Cならば C99 http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf, S・P・ハービソン3世とG・L・スティール・ジュニアのCリファレンスマニュアル みたいなヘビー級のリファレンス)があまり見当たりません。ビギナー向け文献とfortranで研究をするのに必要なリファレンスにギャップがありすぎます。
*3:http://nf.nci.org.au/training/FortranAdvanced/slides/slides.036.html
*4:多分、パンチカードによるプログラムの入力の名残なのでしょう。