- 追加された行はこの色です。
- 削除された行はこの色です。
現在(&lastmod;)作成中です。
既に書いている内容も&color(#ff0000){大幅に変わる};可能性が高いので、注意。
as of &_now; (&counter;)
-------
#contents
-------
* 担当教員 [#d810a8d4]
陰山 聡
* 演習日 [#e84fbb5c]
- 2010.05.06
- 2010.05.13
* 概要と達成目標 [#dcc5c361]
+ 概要
++ why f? (なぜFortran95言語か)
++ f && c (Fotran95とCの同じところ)
++ [演習]Hello, world. コンパイルと実行
++ diff f c (Fotran95とCの違うところ)
++ wonderful f (Fotran95の素晴らしいところ)
++ practical f (Fotran95によるコーディングの実際)
+ 目標
++ この「計算科学演習I」で扱うFotranソースコードが自由に読めること。
++ 2次元拡散方程式を解くコードをFotran95言語の特性を活かして書けるようになること。
* はじめに [#rd9d9a92]
// wiki の練習。コメントのテスト。この文字は見えないはず。見えたら異常。
** エディタとコンパイラ [#oae90ba8]
- Emacsのmajorモードをf90に設定すると便利(f95モードは用意されていない)
-- ミニバッファでf90-modeと打つ (Esc-x f90-mode)
-- 構文の開始と終了ペアリング入力が簡単。tabキー
-- tabキーでソースコード(現在行)を整形。
-- tabキーで何かのend構文自動入力。
-- ^Jで整形改行
- scalar上でのコンパイルコマンドは pgf95 またはpgf90
- フリーのFortran95コンパイラ
-- gfortran (gnu Fortran): http://gcc.gnu.org/fortran/
-- g95: http://www.g95.org/
** Fortran90/95の歴史 [#rd9d9a92]
- ''FORTRAN''(全部大文字の)という言語は存在しない。
--''Fortran90''や''Fortran95''という言語ならある。
- FORTRAN66。
--1966年に標準化。
- FORTRAN77
--1977年に標準化。
-- if/then/else
-- 広まった
- Fortran90
-- 1991年に標準化。
-- 大幅な改訂
- Fortran95
--F90からのマイナーバージョンアップ
Fortran90はFORTRAN77とは大きく違う。違う言語と考えるべき。
~| f95 - f90 | << | f90 - f77 |
* why Fortran90/95? [#qfc92c34]
* なぜFortran90/95を学ぶべきか [#vf0f73f1]
** 正しい理由 [#p0256e61]
+ 計算速度が速い
-- スーパーコンピュータは速さが命
-- スーパーコンピューティングは速さが命。
-- C/C++でもFortranと同じくらい速いコードは書けるが、遅いコードも書けてしまう。
-- 言語としての自由度の違い。自由度が高いとコンパイラが困る。最適化ができなくなる。
--- 言語としての自由度の違いとも言える。
自由度が高いとコンパイラが困る。最適化ができなくなる。
+ 便利
-- 道具(言語)は目的にあったものを
-- 数学的計算にはFortran90/Fotran95が適している
--- '''For'''-mula '''Tran'''-slator
-- 数値計算ライブラリの抱負な蓄積。Legacyなコード。財産。
** 間違った理由 [#o19dac24]
『新しい言語を勉強するのは面倒だ。
FORTRAN77の何の不足もない。これでxx年やってきた。』
『FORTRAN77に何の不満もない。これでxx年やってきた。
(新しい言語を勉強するのは面倒だ。)』
・・・Legacyな人間。時代に2010-1977=33年遅れている。
・・・Legacyなコードはいいが、Legacyな人間は良くない。
時代に30年以上(&_date;-1977)遅れている。
** Fortran90/95に対する偏見 [#u2f56714]
* まずは定番、hello, world プログラム[#k5bb5231]
K&Rに出てくるC言語でのhello, worldプログラムは、
#include <stdio.h>
main () {
printf("hello, world.\n");
}
である。
Fortran90/95ではこうなる。
program hello_world
implicit none
print *, "hello, world."
end program hello_world
このようにCもFotranも手続き型のプログラミング言語なので、
基本的には同じプログラムになる。
いくつかの細かい違いは、
- Fortran90/95ではmainプログラムは program '''name'''として宣言する。'''name'''は任意。
- implicit noneの意味は後述。
- Fortran90/95では標準入出力機能は言語に組み込まれている(ヘッダファイルをインクルードする必要はない(そもそもヘッダファイルというものがない))。
- Fortran90/95では print *で標準出力へ。
- 行末にセミコロン不要。
- 文字列はダブルクォーテーションマーク(")で囲む。(C言語と同じ)
- シングルクウォーテーションマーク(')でも同じ意味。(これはC言語と違う)
** &color(#0000ff){【演習】}; [#r664bdd2]
+ 計算機「scalar」 で、上のFortran90/95プログラムをエディタで入力し、
ファイル名hello_world.f95として保存せよ。
+ ls -l コマンドでファイルを確認せよ。
+ hello_world.f95 をコンパイルせよ。
-- pgf95 hello_world.f95
+ 実行せよ。
-- ./a.out
** Fortran90/95に対する誤解 [#u2f56714]
以下全てFORTRAN66/77と誤解している。
- 変数名は6文字までなんでしょう?・・・そんなことはありません。
- ソースコードは全部大文字なんでしょう?・・・そんなことはありません。
- ソースコードは全部大文字で書くんでしょう?・・・それも可能ですが普通はしません。
- ソースコードは固定形式(7列目から72列目まで)なんでしょう?・・・そんなことはありません。
- 構造体がないんでしょう?・・・あります。普通に使います。
- ポインタがないんでしょう?・・・あります。あまり使う必要はありませんが。
- 再帰呼び出しができなんでしょう?・・・できます。あまり使う機会はありませんが。
- 関数とデータをまとめてひとかたまりにする(クラス化する)ことなんてできないんでしょう?・・・できます。普通にやっています。
- データ/関数の隠蔽(カプセル化)ができないんでしょう?・・・できます。いつもしています。
- 演算子を自分で定義することができなんでしょう?・・・できます。いつもしています。
- 関数や演算子の多重定義ができなんでしょう?・・・できます。いつもしています。
** &color(#0000ff){【演習】}; [#w64efb42]
さきほど作ったhello_world.f95のメッセージ("hello, world.")を自由に変え、
コンパイル&実行せよ。
** Fortran90/95は現代のプログラミング言語である。 [#g3040af6]
- スーパーコンピュータ向けの言語としては最先端
- 特に並列計算機には
- 数値演算や計算機シミュレーションには最適な言語
** まだ納得できないない?では例を一つ [#ra937520]
部屋の中の温度場の分布から平均気温を求めよう。
温度場を3次元float配列 f(nx,ny,nz)で表す。3つの整数nx, ny, nzは不定。
平均気温=全ての格子点(i,j,k)上でのfの値を足して格子点の総数で割る
** &color(#0000ff){【演習】}; [#t9110993]
任意サイズの3次元単精度実数(浮動小数点数)配列を受け取り、
その平均値を返す関数をC言語(またはC++言語)で作れ。
その平均値を返す関数をC言語で作れ。
- scalar上でのCコンパイラ=cc, gcc
** Fortran90/95ではこう書ける。 [#ube6e027]
わずか4行。
このようにわずか4行で書ける。
real function mean_value(f)
real, dimension(:,:,:) :: f
mean_value = sum(f) / (size(f,1)*size(f,2)*size(f,3))
end function mean_value
このプログラムの意味は後で説明する。
** 数式の表現が簡単な例(複素数) [#ada18727]
$e^{i\pi} = -1$
つまり
#ref(e_i_pi.jpg)
をFortran95で書くと、
complex :: i = (0.0,1.0)
real :: pi = 3.141593
print *,' exp(i*pi) = ', exp(i*pi)
** 例(行列の計算) [#h6f4d481]
行列のかけ算はFortran90/95ではmatmulという組み込み関数を使う。
行列の転置をとる組み込み関数transposeも用意されている。
行列のかけ算のためにFortran90/95ではmatmulという組み込み関数が用意されている。
組み込み関数というのはライブラリではなく、言語の一部である。
また、行列の転置をとる組み込み関数transposeも用意されている。
したがって、行列A(例えば10行10列の2次元配列)の転置と別の行列Bの積を計算しそれを行列Cとする計算、つまり
// C = {}^t\!A\, B
#ref(matmul_a_transpose_b.jpg)
をFortran90/95で書くと、
real, dimension(10,10) :: A, B, C
C = matmul(transpose(A),B)
と一行で書ける。
** &color(#0000ff){【演習】}; [#kc80ae9e]
以下のプログラムをmatmul_a_transpose_b.f95というファイルに保存し、
コンパイル&実行せよ。
program matmul_a_transpose_b
implicit none
real, dimension(10,10) :: A, B, C
A = 1.0 ! A(i,j) = 1 for all i and j.
B = 2.0 ! B(i,j) = 2
C = matmul(transpose(A),B)
print *, 'max element of C is ', maxval(C)
end program matmul_a_transpose_b
** 例(級数) [#h6f4d481]
級数
#ref(series_one_forth.jpg)
// \sum_{n=1}^\infty\,\frac{1}{i}\cdot\frac{1}{i+1}\cdot\frac{1}{i+2} = \frac{1}{1}\cdot\frac{1}{2}\cdot\frac{1}{3} + \frac{1}{2}\cdot\frac{1}{3}\cdot\frac{1}{4} + \frac{1}{3}\cdot\frac{1}{4}\cdot\frac{1}{5} + \cdots = \frac{1}{4}
の最初の1000項の和を求めるプログラムも、式をそのまま書けばいい。まさにFormula Translation。
integer, parameter :: nterms = 1000
real, dimension(nterms) :: x, y, z
integer :: i
do i = 1 , nterms
x(i) = 1.0 / i
y(i) = 1.0 / (i+1)
z(i) = 1.0 / (i+2)
end do
print *,'ans = ', sum(x*y*z)
** 例(3次元ベクトル場のエネルギー) [#h2acd79c]
空間中に分布する磁場(3成分のベクトル場)
Bx(100,100,100), By(100,100,100), Bz(100,100,100)
の全磁気エネルギーを計算して出力するには、
print *,' energy = ', sum(Bx**2+By**2+Bz**2)/2
と一行(定義式そのもの)を書けば良い。
* diff f && c : C言語との比較によるFortran90/95入門 [#zf847c1d]
* C言語との比較によるFortran90/95入門 [#zf847c1d]
** 定番 hello, worldプログラム [#k5bb5231]
C言語では
#include <stdio.h>
main () {
printf("hello, world.\n");
}
Fortran90/95では
program hello_world
implicit none
print *, "hello, world."
end program hello_world
- Fortran90/95ではmainプログラムは program nameとして宣言する。nameは任意。
- implicit noneの意味は後述。
- Fortran90/95では標準入出力は組み込まれている(ヘッダファイルをインクルードする必要はない)。
- Fortran90/95では print *で標準出力へ。
- 行末にセミコロン不要。
- 文字列はダブルクォーテーションマーク(")で囲む。(C言語と同じ)
- シングルクウォーテーションマーク(')でも同じ意味。(これはC言語と違う)
** &color(#0000ff){【演習】}; [#r664bdd2]
+ 計算機「scalar」 で、上のFortran90/95プログラムをエディタで入力し、
ファイル名hello_world.f95として保存せよ。
+ ls -l コマンドでファイルを確認せよ。
+ hello_world.f95 をコンパイルせよ。
-- pgf95 hello_world.f95
+ 実行せよ。
-- ./a.out
** ソースコード [#f82498d2]
*** 大文字・小文字 [#dfe20c0f]
- Fortran90/95のソースコードではアルファベットの大文字と小文字は区別しない。
-- kobe と Kobe と KOBE は同じ
-- 文字列変数の中では当然区別される。
*** 自由形式。C言語とほぼ一緒。 [#hb6819be]
- 1行は132文字まで
//*** 自由形式。C言語とほぼ一緒。
//- 1行は132文字まで
*** 数字表現 [#h6d16d46]
- 浮動小数点数
-- 1.
-- 0.1
-- 1e-1
-- 3.141593
-- 3.1415926535897932_dp ! 倍精度。dpは整数。通常8
-- 0.31415926535897932e-1_dp ! 上に同じ
** &color(#0000ff){【演習】}; [#w64efb42]
さきほど作ったhello_world.f95のメッセージ("hello, world.")を自由に変え、
コンパイル&実行せよ。
** &color(#0000ff){【演習】}; [#o302f065]
次のCプログラムはコンパイルエラーになる。
どこにバグがあるが指摘せよ。
main()
{
int dee, daa, doo;
int de, da, do;
dee = de*de;
}
** 予約語 [#r3a28ada]
Fortran90/95には予約語が存在しない。
それが変数名かどうかコンパイラが賢く判断してくれる。
if (end==else) if=0
は文法的に問題ない。
((Fortran90/95では、if (end==else) if=0と書いても文法的に問題ない。))
ユーザ定義の変数名であればコンパイラが賢く判断してくれる。
一方、C言語には32個も予約語がある。
C99ではさらに5個増えた。
** コメント行の書き方 [#l593265a]
C言語では
/* この間がコメント */
である。C99では // から行末までもコメントとなった。
Fortran90/95では
! 一行の中でこの文字以降がコメント( C99やC++、JAVAの//と同じ)
** 定数 [#ha3326c8]
C言語
#define NX 100
Fortran90/95
integer, parameter :: NX = 100
** 数値演算 [#k3d64acf]
四則演算はC言語と同じ
a+b, a-b, a*b, a/b
べき乗は
a**b
** 倍精度浮動小数点数の指定方法 [#f3f36a10]
*** kindパラメータ [#u9ebfddb]
計算科学では実数の物理量を数値的に表現するのに単精度ではなく、
倍精度の浮動小数点数を使うのが普通である。
(単精度実数では精度が不十分なため。)
まは、スーパーコンピュータは単精度浮動小数点ではなく、
倍精度浮動小数点数の演算(四則演算)を高速に処理できるよう設計されている。
従ってFortran90/95言語での倍精度浮動小数点数の表現方法に早めに慣れておくことは重要である。
Fortran90/95で倍精度浮動小数点数を表す方法は幾つかある。
最も簡単でportabilityの高い方法は以下の方法であろう。
まず、単精度浮動小数点数の精度に関係したある整数定数を定義する。
ここではその整数をSPという名前にする。
SPとはSingle Precision(単精度)の頭文字をとったものである。
integer, parameter :: SP = kind(1.0)
次に、このSPを使って単精度として指定した浮動小数点数の2倍の精度を持つ浮動小数点数、
つまり倍精度浮動小数点数に関係したある整数定数(ここではDPという名前)を以下のようにして定義する。
integer, parameter :: DP = selected_real_kind(2*precision(1.0_SP))
この整数定数DPはDouble Precisionを意味する。
こうして定義した二つの整数SPとDPを使って単精度浮動小数点や倍精度浮動小数点数を宣言する。
real(kind=SP) :: a
real(kind=DP) :: b
とすると、aは単精度浮動小数点、bは倍精度浮動小数点数である。
上のプログラムのkind=の部分は省略可能である。
つまり
real(SP) :: a
real(DP) :: b
としてもよい。
数値データの精度指定にもこのSPやDPを使う。
a = 1.0_SP ! 単精度の1.0
b = 1.0_DP ! 倍精度の1.0
サンプルプログラムを見てみよう。
program double_precision_real
implicit none
integer, parameter :: SP = kind(1.0)
integer, parameter :: DP = selected_real_kind(2*precision(1.0_SP))
real(SP) :: a
real(DP) :: b
a = 3.1415926535897323846264338327950288_SP
b = 3.1415926535897323846264338327950288_DP
print *,' a, b = ', a, b
end program double_precision_real
*** &color(#0000ff){【演習】}; [#gbd4ef97]
上のプログラムをdouble_precision_real.f95という名前のファイルに保存し、
コンパイル&実行せよ。
*** &color(#0000ff){【演習】}; [#se6db762]
double_precision_real.f95に現れる二つのkindパラメータSPとDPには実際にはどんな値が入っているか?
** 数字表現 [#h6d16d46]
- 浮動小数点数
-- 1. ! デフォルト精度
-- 0.1_SP ! kindパラメタSPの浮動小数点数
-- 3.141593_DP ! kindパラメタDPの浮動小数点数
-- 0.31415926535897932e-1_DP ! 上に同じ
** 型名 [#udeac7be]
| 型 | C ( C99 ) | Fortran90/95 | 補註 |
| 文字 | char | character | |
| 文字列 | char[n+1] | character(len=n) | nは文字長|
| 整数 | int | integer | integer(kind=4)でも可 |
| 実数 | float | real | real(kind=4)でも可 |
| 倍精度実数 | double | real(kind=8) | kindの整数はシステム依存 |
| 実数 | float | real | real(kind=SP)でも可 |
| 倍精度実数 | double | real(kind=DP) | |
| 「長い」整数 | long | integer(kind=8でも可) | kindの整数はシステム依存 |
| bool | ( _Bool ) | logical | 値は .true. または .false. |
| 複素数 | ( _Complex ) | complex | |
| 構造体 | struct | type | 詳しくは後述 |
** 構造体 (derived type) [#r0bada23]
C言語の構造体とほとんど同じである。下の例をみよ。
program type
implicit none
type student
character(len=20) :: first_name, last_name
integer :: age
end type student
type(student) :: st
st = student("Albert", "Einsten", 19)
print *,st%first_name, st%last_name, st%age
end program type
構造体のメンバにアクセスするのに%を使う。
** 定数 [#ha3326c8]
C言語
#define NX 100
** &color(#0000ff){【演習】}; [#j197c41e]
上のプログラムをtype.f90に保存し、コンパイル&実行せよ。
Fortran90/95
integer, parameter :: NX = 100
** &color(#0000ff){【演習】}; [#h83d476b]
student型の構造体変数をもう一つ(例えばst2という名前)を作り、
stのデータをst2にコピーせよ。
** 宣言 [#heae7ea1]
C言語では変数の宣言はブロックの先頭にまとめて置く。(C99やC++はもっと自由だが。)
Fortran90/95でも同じ。
** 暗黙の型宣言 [#i67dc5f6]
Fortran90/95ではimplicit noneを省略すると「暗黙の型宣言」をしたことになる。
暗黙の型宣言とは、次の6つのアルファベット i,j,k,l,m,n で始まる変数は整数である等のルールである。
バグが入りやすいので、暗黙の型宣言は使わない方が良い。
&color(#ff0000){Fortran90/95プログラムでは常にimplicit none宣言をすること。}
** 暗黙の型宣言はなぜバグが入りやすいか [#ife3cc0b]
宣言したつもりのない変数を間違って使っていても気がつかない可能性がある。
例えば、次のプログラムにはバグがある。すぐに見つけられるか?
program use_implicit_none
integer :: fresh_meat
flesh_meat = 100 ! yen
print *, "today's price = ", fresh_meat
end program use_implicit_none
** &color(#0000ff){【演習】}; [#i7e62dcd]
上のプログラムをuse_implicit_nene.f95というファイルに書き込み、コンパイル&実行せよ。
- バグがあるのにコンパイラは教えてくれないだけでなく、
何事もないかのように正常終了する。しかも、それらしい答えを返す。
バグがあることに気がつかない!
** &color(#0000ff){【演習】}; [#n158e0a4]
今作ったuse_implicit_nene.f95の2行目(program ...の次の行)に
implicit noneと書いてから、コンパイルせよ。
** 行の継続 [#fd5240b0]
行末に&をつけると継続行
a = b + c + &
d + f
** 配列 [#rb6c9a8a]
*** 1次元配列 [#p525880e]
C言語
int array01[NX];
メモリ上に、array01[0], array01[1], array01[2], ..., array01[NX-1]と並ぶ。
** セミコロン [#rf3da92e]
セミコロンは改行と同じ
tmp = right
right = left
left = tmp
と
tmp = right; right = left; left = tmp
は同じ。
Fortran90/95
** 1次元配列 [#p525880e]
C言語では
array01[NX];
が大きさNXの1次元配列である。
メモリ上に、array01[0], array01[1], array01[2], ..., array01[NX-1]と並んでいる。
Fortran90/95では
integer, dimension(NX) :: array01
メモリ上に、array01(1), array01(2), array01(3), ..., array01(NX)と並ぶ。
と宣言するとメモリ上に、array01(1), array01(2), array01(3), ..., array01(NX)と並ぶ。
&color(#ff0000){配列のインデックスは1から始まる。};
** &color(#0000ff){【演習】}; [#e1e74b6e]
大きさNXの1次元整数配列array_01を作り、
各要素に1,2,3,...,NXを代入した上で、
全要素の和をとるFortran90/95プログラムを書け。
*** 2次元配列 [#xa06a740]
C言語
int array02[NY][NX];
** 2次元配列 [#xa06a740]
厳密に言えばC言語では2次元配列はない。
あるのは「配列の配列」である。
つまり二つの整数インデックスiとjを使って
array02[j][i]
という形でアクセスできる量はある。
Fortran90/95
array02のサイズ(インデックスiとjの範囲)は実行時に不定として、
array02をまるごと引数で受け取る関数がC言語では作れない。
従ってこれを2次元配列とは呼び難い。
** メモリ空間中での2次元配列の各要素の位置 [#rf14cf18]
上記のarray02は、
array02[0][0], array02[0][1], array02[0][2], ..., array02[0][NX-1]
と並び、そして
(それに続く場合もあるし、
まったく別の場所にあるかもしれない)ある位置から、
array02[1][1], array02[1][1], array02[1][2], ..., array02[1][NX-1]
という順番で並ぶ。
一方、
Fortran90/95ではarray02(i,j)という形でアクセスできる量は本物の2次元配列である。
サイズがわかっている場合には
integer, dimension(NX,NY) :: array02
と宣言する。
不明の場合には、
integer, dimension(:,:) :: array02
と宣言し、後ほど(実行時に)メモリをallocateする。
メモリ上では、array02の要素は
array02(1,1), array02(2,1), array02(3,1), ..., array02(NX,1), array02(1,2),...
に全ての要素(NX*NY個)が連続して並ぶ。
*** 3次元配列 [#o2df7a6b]
C言語
int array03[NZ][NY][NX];
配列の要素がメモリ空間中に連続してあることは、
メモリへのアクセス速度が極めて重要となるスーパーコンピューティングでは極めて重要である。
Fortran90/95
** 3次元配列 [#o2df7a6b]
C言語では
array03[k][j][i]
が上の意味での「擬似」3次元配列である。
Fortran90/95では
array03[i][j][k]
が3次元配列で、
宣言は
integer, dimension(NX,NY,NZ) :: array03
または
integer, dimension(:,:,:) :: array03
とする。
** 行の継続 [#fd5240b0]
行末に&をつけると継続行
a = b + c + &
d + f
** if構文 [#rbc6cf7b]
if文はC言語と似ている。下の例を見れば一目瞭然であろう。
if ('''criterion''') then
'''action'''
end if
** セミコロン [#rf3da92e]
セミコロンは改行と同じ
tmp = right
right = left
left = tmp
と
tmp = right; right = left; left = tmp
は同じ。
'''action'''が1行で書けるなら
if ( '''criteron''' ) '''action'''
ともできる。
else-if構文も簡単である。
if ('''criterion_1''') then
'''action_1'''
else if ('''criterion_2''' ) then
'''action_2'''
else if ('''criterion_3''' ) then
'''action_3'''
end if
** 論理式 [#h83d14fd]
これも下の例を見ればわかるであろう。
if (a==b) then ! aとbが等しい
if (a>b) then
if (a>=b) then
if (a<b) then
if (a<=b) then
if (a/=b) then ! aとbが等しくない
** 論理値 [#l2635332]
bool変数はlogicalという型名を持ち、
.true.
か
.false.
の二つをとる。
logical :: a, b, c
a = .true.
b = .false.
c = a .and. b
c = a .or. b
** case 構文 [#gf5c7a38]
C言語でいうswitch文である。
select case ('''case expression''')
case ('''case selector 1''')
'''action 1'''
case ('''case selector 2''')
'''action 2'''
case ('''case selector 3''')
'''action 3'''
.
.
case default ! なくても良い。
'''default action'''
end select
C言語では普通breakが必要だがFortran90/95のcase文には不要である。
** &color(#0000ff){【演習】}; [#t8418e85]
次のプログラムをcase.f95に保存し、実行せよ。
program case
implicit none
integer :: month
month = 6
select case (month)
case (1)
print *,'January.'
case (2)
print *,'February'
case (3:5)
print *,'Spring'
case default
print *,'Other than spring.'
end select
end program case
** &color(#0000ff){【演習】}; [#w67c419b]
後述するが、Fortran90/95は文字列の取り扱いがC言語よりもずっと簡単である。
例えば、長さの違う文字列をselect case構文で簡単に場合分けできる。
program case_string
implicit none
character(len=*), parameter :: color = 'white'
select case (color)
case ('aka')
print *, 'red'
case ('ao')
print *, 'blue'
case ('midori')
print *, 'green'
case default
print *, "I don't know the color."
end select
end program case_string
** 繰り返し構文(doループ) [#o6d3ed06]
C言語でいうfor文である。
*** incrementが1の場合のdo-loop [#ubf7f370]
program do_loop
implicit none
integer :: i
do i = 1 , 100
print *, ' i = ', i
end do
end program do_loop
*** incrementが1以外の場合のdo-loop [#p04b7d19]
program do_loop
implicit none
integer :: i
do i = 1 , 100 , 2
print *, ' i = ', i
end do
end program do_loop
*** &color(#0000ff){【演習】}; [#gd806660]
- 上のプログラムをdo_loop.f95に保存し、increment値(上の場合2)を変えて実行せよ。
- do_loop.f95のdo文を
do i = 100 , -100 , -2
として実行せよ。
** exitとcycle [#k7acbe39]
整数のカウンターのないdo-loopも可能である。
do
...
end do
このままだと無限ループになるので、通常はある条件を満たせばループから抜け出すようにする。
C言語だとbreak文でループから抜け出す。(正確には最も内側のループから抜け出す。)
Fortran90/95でbreakに相当するのはexitである。
do
...
if ('''condition''') exit !---+
end do ! | ループから抜け出す。
!<--+
C言語ではループの先頭に戻る時にはcontinue文を使うが、
それに相当するのはFortran90/95ではcycleである。
do !<--+
... ! | 戻る
if ('''condition''') cycle !---+
...
end do
*** &color(#0000ff){【演習】}; [#z048bfe1]
if文、select case文、do文を使って自由にプログラムを作ってみよ。
** 関数とサブルーチン [#d2187b84]
C言語と同様に関数(function)という手続き(procedure)は、
Fortran90/95プログラムの重要な構成部品である。
関数の本来の機能は入力に基づいて出力を返すものであるが、
出力が不要な場合、C言語ではvoid関数とする。
このように引数を渡して何らかの処理をするが、
返り値が存在しない手続きをFortran90/95ではsubroutineとしてfunctionと区別する。
関数の使い方は以下の例を見ればわかるであろう。
*** &color(#0000ff){【演習】}; [#a4a2bbc1]
- 次のプログラムをfunction01.f95として保存し、実行せよ。
integer function next_int(i)
integer, intent(in) :: i
next_int = i+1
end function next_int
program function01
implicit none
integer, external :: next_int
print *, 'ans = ', next_int(-100)
end program function01
intent(in)は引数iが入力引数であり、関数next_intの内部で変更されることがないことを保証するものである。
- 関数を以下のように内部副プログラムとしてメインの中に含むこともできる。
次のプログラムをfunction02.f95として保存し、実行せよ。
(内部副プログラムについては後述。)
program function02
implicit none
print *, 'ans = ', next_int(-100)
contains
integer function next_int(i)
integer, intent(in) :: i
next_int = i+1
end function next_int
end program function02