- 追加された行はこの色です。
- 削除された行はこの色です。
現在(&lastmod;)作成中です。
既に書いている内容も&color(#ff0000){大幅に変わる};のは間違いないので注意。
// 現在(&lastmod;)作成中です。
// 既に書いている内容も&color(#ff0000){大幅に変わる};のは間違いないので注意。
-------
神戸大学 大学院システム情報学研究科 計算科学専攻 陰山 聡
-------
#contents
-------
* アンケート(その01): 前回の講義について [#x99181c7]
#vote(簡単すぎた, 難しすぎた, ちょうどよかった)
* アンケート(その02): C言語との比較による説明の仕方について [#bad5d153]
#vote(Cとの比較で良い, CよりもJavaと比較して欲しい, 他の言語と比較はない方がわかりやすい)
* 倍精度浮動小数点数の指定方法 [#b90f3b3c]
** kindパラメータ [#wbd486cb]
* 倍精度浮動小数点数の指定方法 [#l4db6df4]
** kindパラメータ [#fdbf591c]
計算科学では実数の物理量を数値的に表現するのに単精度ではなく、
倍精度の浮動小数点数を使うのが普通である。
(単精度実数では精度が不十分なため。)
まは、スーパーコンピュータは単精度浮動小数点ではなく、
倍精度浮動小数点数の演算(四則演算)を高速に処理できるよう設計されている。
従って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){【演習】}; [#m1ea2638]
* &color(#0000ff){【演習】}; [#y830e89a]
上のプログラムをdouble_precision_real.f95という名前のファイルに保存し、
コンパイル&実行せよ。
* &color(#0000ff){【演習】}; [#u0b459f3]
* &color(#0000ff){【演習】}; [#bc458a3c]
double_precision_real.f95に現れる二つのkindパラメータSPとDPには実際にはどんな値が入っているか調べよ。
* 数字表現 [#x3e65148]
* 数字表現 [#paf2a44f]
- 浮動小数点数
-- 1. ! デフォルト精度
-- 0.1_SP ! kindパラメタSPの浮動小数点数
-- 3.141593_DP ! kindパラメタDPの浮動小数点数
-- 0.31415926535897932e-1_DP ! 上に同じ
* アンケート:C言語について [#p6cfb1d3]
#vote(Cはよく知っている, Cは分からない(忘れた))
* 型名 [#hca8af14]
* 型名 [#n53f3c8b]
| 型 | C ( C99 ) | Fortran90/95 | 補註 |
| 文字 | char | character | |
| 文字列 | char[n+1] | character(len=n) | nは文字長|
| 整数 | int | integer | integer(kind=4)でも可 |
| 実数 | 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) [#o99419e2]
* 構造体 (derived type) [#qa3de9bb]
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
構造体のメンバにアクセスするのに%を使う。
構造体の入れ子も可能である。
構造体の配列も可能である。
構造体を構造体に代入(コピー)することも可能である。
* &color(#0000ff){【演習】}; [#na2ba0de]
* &color(#0000ff){【演習】}; [#o52e6249]
上のプログラムをtype.f90に保存し、コンパイル&実行せよ。
* &color(#0000ff){【演習】}; [#od4538f0]
* &color(#0000ff){【演習】}; [#u188feef]
student型の構造体変数をもう一つ(例えばst2という名前)を作り、
stのデータをst2にコピーした上で、要素の一部(例えばage)を変更し、st2を出力せよ。
* 宣言 [#q0368b92]
* 宣言 [#obf61890]
C言語では変数の宣言はブロックの先頭にまとめて置く。(C99やC++はもっと自由だが。)
Fortran90/95でも同じ。
* 暗黙の型宣言 [#tc2c33ff]
* 暗黙の型宣言 [#bd5cae3a]
Fortran90/95ではimplicit noneを省略すると「暗黙の型宣言」をしたことになる。
暗黙の型宣言とは、次の6つのアルファベット i,j,k,l,m,n で始まる変数は整数である等のルールである。
使わない方がいい機能なので詳細は知らなくて良いだろう。
バグが入りやすいので、暗黙の型宣言は使わない方が良い。
Fortran90/95プログラムでは&color(#ff0000){常にimplicit none宣言をすること。};
* 暗黙の型宣言はなぜバグが入りやすいか [#w75e86a6]
* 暗黙の型宣言はなぜバグが入りやすいか [#v35a2f01]
宣言したつもりのない変数を間違って使っていても気がつかない可能性がある。
例えば、次のプログラムにはバグがある。すぐに見つけられるか?
program use_implicit_none
integer :: fresh_meat
flesh_meat = 100 ! yen
print *, "today's price = ", fresh_meat
end program use_implicit_none
* &color(#0000ff){【演習】}; [#xd8471a3]
* &color(#0000ff){【演習】}; [#c09d93cc]
上のプログラムをuse_implicit_nene.f95というファイルに書き込み、コンパイル&実行せよ。
- バグがあるのにコンパイラは教えてくれないだけでなく、
何事もないかのように正常終了する。しかも、それらしい答えを返す。
つまりバグがあることにすら気がつかない!
* &color(#0000ff){【演習】}; [#i9399e76]
* &color(#0000ff){【演習】}; [#y49128e4]
今作ったuse_implicit_nene.f95の2行目(program ...の次の行)に
implicit noneと書いてから、コンパイルせよ。
* 行の継続 [#u8a3a60c]
* 行の継続 [#k3a8a03c]
行末に&をつけると継続行
a = b + c + &
d + f
* セミコロン [#y0eac818]
* セミコロン [#sd2bbead]
セミコロンは改行と同じ
tmp = right
right = left
left = tmp
と
tmp = right; right = left; left = tmp
は同じ。
* 1次元配列 [#z78a9b40]
* 1次元配列 [#y7796d7c]
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)と並ぶ。
&color(#ff0000){配列のインデックスは1から始まる。};
* &color(#0000ff){【演習】}; [#db21d215]
* &color(#0000ff){【演習】}; [#tc9edde9]
大きさNXの1次元整数配列array_01を作り、
各要素に1,2,3,...,NXを代入した上で、
全要素の和をとるFortran90/95プログラムを書け。
* 2次元配列 [#ncd4eeca]
* 2次元配列 [#ta41df45]
厳密に言えばC言語には2次元配列はない。
あるのは「配列の配列」である。
つまり二つの整数インデックスiとjを使って
array02[j][i]
という形でアクセスできるものである。
このarray02を2次元配列とは呼べないということは、
array02のサイズ(インデックスiとjの範囲)は実行時に不定として、
array02をまるごと引数で受け取る関数がC言語では作れないことを考えれば納得出来るであろう。
* メモリ空間中での2次元配列の各要素の位置 [#a07128c6]
* メモリ空間中での2次元配列の各要素の位置 [#f00a07ce]
上記の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]
array02[1][0], 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次元配列 [#s8cc6177]
* 3次元配列 [#m9257d8d]
C言語では
array03[k][j][i]
が上の意味での「擬似」3次元配列である。
Fortran90/95では
array03[i][j][k]
が3次元配列で、
宣言は
integer, dimension(NX,NY,NZ) :: array03
または
integer, dimension(:,:,:) :: array03
とする。
* if構文 [#i308f811]
* if構文 [#c3f2392d]
if文はC言語とほとんど同じ。下の例を見れば一目瞭然であろう。
if (criterion) then
action
end if
'''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
* 関係演算子 [#g5e70b10]
* 関係演算子 [#d5ead8d8]
これも下の例を見ればわかるであろう。
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が等しくない
* 関係演算子(古い書き方) [#r7c520aa]
* 関係演算子(古い書き方) [#za8bafd7]
上の関係演算子はそれぞれ下のようにも書ける。
if (a.eq.b) then ! aとbが等しい
if (a.gt.b) then
if (a.ge.b) then
if (a.lt.b) then
if (a.le.b) then
if (a.ne.b) then ! aとbが等しくない
* 論理値 [#f694601d]
* 論理値 [#m3a40219]
bool変数はlogicalという型名を持ち、
.true.
か
.false.
の二つをとる。
logical :: a, b, c
a = .true.
b = .false.
c = a .and. b
c = a .or. b
c = .not.a
* 繰り返し構文(doループ) [#uedfc000]
* 繰り返し構文(doループ) [#qc16302d]
C言語でいうfor文である。
** incrementが1の場合のdo-loop [#me73416e]
** incrementが1の場合のdo-loop [#bfe407c1]
program do_loop
implicit none
integer :: i
do i = 1 , 100
print *, ' i = ', i
end do
end program do_loop
** incrementが2の場合のdo-loop [#d5230d82]
** incrementが2の場合のdo-loop [#l4008acf]
program do_loop
implicit none
integer :: i
do i = 1 , 100 , 2
print *, ' i = ', i
end do
end program do_loop
* &color(#0000ff){【演習】}; [#zed411bf]
* &color(#0000ff){【演習】}; [#ube10561]
- 上のプログラムをdo_loop.f95に保存し、increment値(上の場合2)を自由に変えて実行せよ。
- do_loop.f95のdo文を
do i = 100 , -100 , -2
として実行せよ。
* exitとcycle [#zf2e6992]
* exitとcycle [#zffdc01d]
整数のカウンターのない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){【演習】}; [#l1794fba]
//* &color(#0000ff){【演習】};
if文、select case文、do文を使って自由にプログラムを作ってみよ。
// if文、select case文、do文を使って自由にプログラムを作ってみよ。
* 関数 [#r1a5bee4]
* 関数 [#yed71e67]
C言語と同様に関数(function)という手続き(procedure)は、
Fortran90/95プログラムの重要な構成部品である。
関数の使い方は以下の例を見ればわかるであろう。
* &color(#0000ff){【演習】}; [#u6abbb06]
* &color(#0000ff){【演習】}; [#c16bf24c]
- 次のプログラムをfunction01.f95として保存し、実行せよ。
integer function next_int(i)
implicit none
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
* サブルーチン [#j858d884]
* サブルーチン [#p063b87a]
関数の本来の機能は入力に応じて出力を返すものであるが、
出力(返り値)が不要な場合も多い。
C言語ではこのような場合void関数とするが、
Fortran90/95ではvoid関数をsubroutineと呼ぶ。
サブルーチンを呼び出すにはcall '''subroutine名'''とする。
以下の例をみよ。
subroutine next_int(input, output)
implicit none
integer, intent(in) :: input
integer, intent(out) :: output
output = input + 1
end subroutine next_int
program subroutine01
implicit none
integer :: i, j
i = 10
call next_int(i,j)
print *, 'ans = ', j
end program subroutine01
* 値渡しと参照渡し [#n02083fe]
* 値渡しと参照渡し [#u46d80d7]
C言語は値渡し、Fortran90/95は参照渡しである。
C言語が値渡しであることは、以下の例でkの値が変わっていないことから確認できる。
void pass(int i)
{
i = 0;
printf("i=%d\n", i);
}
main()
{
int k = 2010;
printf("before: k = %d\n", k);
pass(k);
printf("after: k = %d\n", k);
}
これと'''似た'''プログラム(同じではない)をFortran90/95で書くと以下のようになる。
subroutine pass(i)
implicit none
integer :: i
i = 0;
print *, "i=", i
end subroutine pass
program call_by_reference
implicit none
integer :: k = 2010
print *, "before: k = ", k
call pass(k)
print *, "after: k = ", k
end program call_by_reference
このプログラムではkの値はサブルーチンpassを読んだ後に変更されている。
* &color(#0000ff){【演習】}; [#w00a63d7]
* &color(#0000ff){【演習】}; [#me97009f]
上の二つのプログラムをそれぞれcall_by_value.c、call_by_reference.f95という名前で保存し、実行せよ。
* 入出力属性 [#qc7a9554]
* 入出力属性 [#s118d01f]
値渡しに比べて参照渡しは値をコピーする必要がないので実行が速い。
だが、上のcall_by_reference.f95プログラムで見たように、
引数として渡した変数がそのサブルーチン内部で勝手に(誤って)変更されると見つけにくいバグになり、危険である。
このような事故を防ぐためにFortran90/95には引数に入出力属性をつけることができる。
サブルーチンincrementの引数iは入力属性をもつと指定すると(下のプログラムの2行目)、
コンパイラがエラーを出してくれる。
subroutine increment(i) ! コンパイルエラーを出してくれる
integer, intent(in) :: i
i = 0;
print *, "i=", i
end subroutine increment
program call_by_reference
implicit none
integer :: k = 2010
print *, "before: k = ", k
call increment(k)
print *, "after: k = ", k
end program call_by_reference
* &color(#0000ff){【演習】}; [#gd1ca64f]
* &color(#0000ff){【演習】}; [#ff504025]
call_by_reference.f95を上のように変更してコンパイルせよ。
* 入出力属性の種類 [#ded0a362]
* 入出力属性の種類 [#t1dff30a]
入出力属性には以下の3種類がある。
| intent(in) | 入力 | その手続き内で値は変更されない変数 |
| intent(out) | 出力 | その手続き内で値が設定される変数 |
| intent(inout) | 入出力 | 両者の混合。デフォルト。 |
バグの混入を防ぐために、Fortran90/95プログラムでは&color(#ff0000){すべての引数に入出力属性をつける};ことを強く勧める。
* case 構文 [#pe0d97f8]
* case 構文 [#b53c56a3]
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){【演習】}; [#e8234997]
* &color(#0000ff){【演習】}; [#zc592724]
次のプログラムを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 season'
end select
end program case
* 文字列の比較 [#qa2a3c2b]
* 文字列の比較 [#y51b28ed]
C言語では二つの文字列が等しいかどうかを判断するためにわざわざstrcmpを呼ぶ。
Fortran90/95では
if (string1==string2)
や
if ( response == "what do you mean?" )
などと書ける。
従って長さの違う文字列も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
* &color(#0000ff){【演習】}; [#pecc14bd]
* &color(#0000ff){【演習】}; [#v9477cc5]
上のプログラムをcase_string.f95というファイルに保存し、実行せよ。
* 様々な文字列操作 [#gbeafb3a]
* 様々な文字列操作 [#lac97977]
ここでFortran90/95における文字列操作機能を紹介しておく。
参考までにCでの同様な操作をコメントに書いておいた。
program strings
implicit none
character(len=*), parameter :: string01 = 'Computational'
character(len=*), parameter :: string02 = 'Science'
character(len=30) :: message30
print *, ' length of string01 = ', len(string01) ! strlen(string01)
print *, ' string01==string02 = ', string01==string02 ! strcmp(string01,string02)
print *, ' string01//string02 = ', string01//string02 ! strcat(string01,string02)
print *, ' string01//string02 = ', string01//string02 ! strcat(string01,string02)
print *, ' string01//string02//string02 = ', string01//string02//string02 ! ?
message30 = string01 ! strcpy(message,string01)
print *, ' message in 30 characters = ', message30
print *, ' length of message30 is = ', len(message30)
print *, ' length of message30 is = ', len_trim(message30)
print *, ' message30(10:13) = ', message30(10:13)
print *, ' message30//string02 = ', message30//string02
print *, ' trim(message30)//string02 = ', trim(message30)//string02
end program strings
* 配列演算機能: スカラーの代入 [#q59330e0]
* 配列演算機能: スカラーの代入 [#nf3b66e0]
まずはC言語版を見る。
/* array01.c */
#define NX 10
#define NY 12
#define NZ 14
main()
{
int i, j, k;
double A[NZ][NY][NX];
for (k=0; k<NZ; k++) {
for (j=0; j<NY; j++) {
for (i=0; i<NX; i++) {
A[k][j][i] = 0.0;
A[k][j][i] = 0.0_DP;
}
}
}
}
同じことをFortran90/95で「下手に」書けばこうなる。
同じことをFortran90/95で「丁寧に」書けばこうなる。
!
! array01.f95
!
module constants
implicit none
integer, parameter :: SP = kind(1.0)
integer, parameter :: DP = selected_real_kind(2*precision(1.0_SP))
integer, parameter :: NX = 10
integer, parameter :: NY = 12
integer, parameter :: NZ = 14
end module constants
program array01
use constants
implicit none
integer :: i, j, k
real(DP), dimension(NX,NY,NZ) :: A
do k =1 , NZ
do j = 1 , NY
do i = 1 , NX
A(i,j,k) = 0.0
A(i,j,k) = 0.0_DP
end do
end do
end do
end program array01
普通はこう書く。module constantsは同じなのでmainプログラムだけ示す。
program array01
use constants
implicit none
integer :: i, j, k
real(DP), dimension(NX,NY,NZ) :: A
A = 0.0
end program array01
* 配列演算機能: 配列の要素毎の計算(その1) [#re2501a5]
* 配列演算機能: 配列の要素毎の計算(その1) [#ja7bca1d]
C言語で
/* array02.c */
#define NX 10
#define NY 12
#define NZ 14
main()
{
int i, j, k;
double A[NZ][NY][NX];
double B[NZ][NY][NX];
double C[NZ][NY][NX];
for (k=0; k<NZ; k++) {
for (j=0; j<NY; j++) {
for (i=0; i<NX; i++) {
A[k][j][i] = 3*B[k][j][i]-C[k][j][i];
}
}
}
}
Fortran90/95ではこうなる。「下手」に書いた方法をコメントで示した。
Fortran90/95ではこうなる。「丁寧に」書いた方法をコメントで示した。
(module constantsは同じ)
program array02
use constants
implicit none
integer :: i, j, k
real(DP), dimension(NX,NY,NZ) :: A, B, C
!------------------------------------------
! do k =1 , NZ
! do j = 1 , NY
! do i = 1 , NX
! A(i,j,k) = 3*B(i,j,k)-C(i,j,k)
! end do
! end do
! end do
!------------------------------------------
A = 3*B-C
end program array02
* 配列演算機能: 配列の要素毎の計算(その2) [#j62a06a1]
* 配列演算機能: 配列の要素毎の計算(その2) [#wf0e786a]
割り算でも同じである。
!
! array03.f95
!
module constants
implicit none
integer, parameter :: SP = kind(1.0)
integer, parameter :: DP = selected_real_kind(2*precision(1.0_SP))
integer, parameter :: NX = 10
integer, parameter :: NY = 12
integer, parameter :: NZ = 14
end module constants
program array03
use constants
implicit none
integer :: i, j, k
real(DP), dimension(NX,NY,NZ) :: A, B, C
!------------------------------------------
! do k =1 , NZ
! do j = 1 , NY
! do i = 1 , NX
! A(i,j,k) = B(i,j,k)/C(i,j,k)
! end do
! end do
! end do
!------------------------------------------
A = B/C
end program array03
* 配列演算機能: 組み込み関数の引数 [#m4955b1f]
Fortran90/95には数学関係の豊富な組み込み関数が用意されている。
それらの引数に配列を渡すと配列の全要素についてその関数を適用してくれる。
たとえば、C言語で
/*
* array04.c
* cc -lm array04.c
*/
#include <math.h>
#define NX 10
#define NY 12
#define NZ 14
main()
{
int i, j, k;
double A[NZ][NY][NX];
double B[NZ][NY][NX];
for (k=0; k<NZ; k++) {
for (j=0; j<NY; j++) {
for (i=0; i<NX; i++) {
A[k][j][i] = sin(B[k][j][i]);
}
}
}
}
というプログラムは、Fortran90/95では下のようになる。
!
! array04.f95
!
module constants
implicit none
integer, parameter :: SP = kind(1.0)
integer, parameter :: DP = selected_real_kind(2*precision(1.0_SP))
integer, parameter :: NX = 10
integer, parameter :: NY = 12
integer, parameter :: NZ = 14
end module constants
program array04
use constants
implicit none
integer :: i, j, k
real(DP), dimension(NX,NY,NZ) :: A, B
!------------------------------------------
! do k =1 , NZ
! do j = 1 , NY
! do i = 1 , NX
! A(i,j,k) = sin(B(i,j,k))
! end do
! end do
! end do
!------------------------------------------
A = sin(B)
end program array04
* 便利な配列演算(その01) [#m6700310]
そろそろFortran90/95にも慣れてきたと思うので、
C言語との比較ではなく、Fortran90/95言語で説明する
((ブートストラップ解説とでも言うべきか。))。
real(DP), dimension(NX,NY) :: array02d
real(DP), dimension(NX,NY,NZ) :: array03d
do j = 1 , NY
do i = 1 , NX
array03d(i,j,1) = arrya02d(i,j)
end do
end do
というプログラムは、コロンを使えば次のように簡潔に記述できる。
real(DP), dimension(NX,NY) :: array02d
real(DP), dimension(NX,NY,NZ) :: array03d
array03d(:,:,1) = arrya02d(:,:)
と書ける。
* 便利な配列演算(その02) [#i01d35f5]
real(DP), dimension(10) :: A
real(DP), dimension(15) :: B
do i = 1 , 10
A(i) = B(i+5)
end do
は、
real(DP), dimension(10) :: A
real(DP), dimension(15) :: B
A(:) = B(6:15)
と書ける。多次元でも同様で、
A(11:15,21:30) = B(111:115,121:130),
などと書ける。両辺の配列の各次元のサイズが一致している必要がある。
* 配列を返す関数 [#nf404a23]
組み込み関数だけでなく、
「配列を受け取って配列を返す」関数を自分で定義することができる。
下に示すのは結構本格的なプログラムである。
乱数を生成する組み込み関数(サブルーチン)random_numberをcallしている。
意味は明確であろう。
!
! array_func.f95
!
module constants
implicit none
integer, parameter :: SP = kind(1.0)
integer, parameter :: DP = selected_real_kind(2*precision(1.0_SP))
integer, parameter :: NX = 10
integer, parameter :: NY = 12
end module constants
module array_sample
use constants
implicit none
private
public :: shift
contains
function average(a)
real(DP), dimension(NX,NY), intent(in) :: a
real(DP) :: average
average = sum(a) / (NX*NY)
end function average
function shift(a)
real(DP), dimension(NX,NY), intent(in) :: a
real(DP), dimension(NX,NY) :: shift
real(DP) :: mean
mean = average(a)
shift(:,:) = a(:,:) - mean
end function shift
end module array_sample
program array_func
use constants
use array_sample
implicit none
integer :: i, j
real(DP) :: random
real(DP), dimension(NX,NY) :: array01, array02
do j = 1 , NY
do i = 1 , NX
call random_number(random)
array01(i,j) = random
end do
end do
array02 = shift(array01)
end program array_func
* 便利な組み込み関数 [#q19cbba1]
** ベクトルの内積 [#pc6352cb]
v1とv2を1次元配列(ベクトル)として、内積は
value = dot_product(v1,v2)
と書ける。
sum = 0.0_DP
do i = 1 , NX
sum = sum + v1(i)*v2(i)
end do
といった計算を一度にやってくれる便利な関数である。
** 行列・ベクトルのかけ算 [#r3281daf]
a, bを行列(2次元配列)、
v1, v2をベクトル(1次元配列)として、
c = matmul(a,b) ! 行列aとbのかけ算
v2 = matmul(c,v1) ! 行列かけるベクトル
などと書ける。
** 行列の転置 [#q8eaa65e]
transpose(a)
** 配列の全要素中の最大値と最小値 [#o448850c]
maxval(a)
minval(a)
** 配列の全要素の和 [#u30400dc]
sum(a)
** 配列の全要素の積 [#z507da40]
product(a)
** 配列の上下限の大きさ [#t368b1f3]
a(3:10,4:20)
に対して
lbound(a,dim=1)
は3を
ubound(a,dim=2)
は20を返す。
size(a,dim=1)
は8である。
意味は明白であろう。
* 不定サイズの配列とメモリ割当 [#i1657563]
サイズが不定の配列aは以下のようにallocatableをつけて宣言する。
real(DP), dimension(:,:), allocatable :: a
実際にaを使う時には
allocate(a(10,20))
などとallocate文を使ってメモリに確保する。メモリを開放するには
deallocate(a)
とする。
* サイズが不定の配列の渡し方 [#l1f38380]
実行時に配列サイズが決まる3次元配列をサブルーチンと関数に渡すサンプルプログラムを下に示す。
!
! array_subroutine.f95
!
module constants
implicit none
integer, parameter :: SP = kind(1.0)
integer, parameter :: DP = selected_real_kind(2*precision(1.0_SP))
end module constants
module array
use constants
implicit none
contains
function average(a)
real(DP), dimension(:,:), intent(in) :: a
real(DP) :: average
average = sum(a) / (size(a,1)*size(a,2))
end function average
subroutine shift(a)
real(DP), dimension(:,:), intent(inout) :: a
real(DP) :: mean
mean = average(a)
a = a - mean ! or you can write a(:,:) = a(:,:) - mean
end subroutine shift
end module array
program array_subroutine
use constants
use array
implicit none
integer :: nx, ny
integer :: i, j
real(DP) :: random
real(DP), dimension(:,:), allocatable :: array2d
call random_number(random)
nx = 10*random; ny = nx
print *,' nx = ', nx
allocate(array2d(nx,ny))
do j = 1 , ny
do i = 1 , nx
call random_number(random)
array2d(i,j) = random
end do
end do
print *,' before: ', average(array2d)
call shift(array2d)
print *,' after: ', average(array2d)
! call report
end program array_subroutine
* &color(#0000ff){【課題1】}; [#w3ea1257]
(1) 上のarray_subroutine.f95プログラムの最後から2行目のコメントをはずして
reportというサブルーチンをcallするようにした上で、
(2) 次のサブルーチンを module arrayの中に入れ、
subroutine report
character(len=8) :: date
character(len=10) :: time
call date_and_time(date,time)
print *,date(3:8)//'/'//time(1:2)//':'//time(3:4)//':'//time(5:6)
end subroutine report
(3) コンパイル&実行してその実行結果を確認し、
(4) 問題がなさそうであれば(出力が4行であれば)
./a.out | mail kage
としてその実行結果を送信せよ。("scalar"でのみ有効。)
* &color(#0000ff){【課題2】}; [#z1d7aee6]
上のarray_subroutine.f95プログラム中のサブルーチンshiftは
「配列aの全要素の平均値がゼロとなるように配列aを変更する」
という機能を持っている。
この機能を自分の好きなように変えて新しいサブルーチンにした上で、
+ どのような機能を持ったサブルーチンに変更したという説明
+ 変更したshiftルーチンのソースコード(他の部分は不要)
+ 実行結果
の3つをレポートにまとめて次回の演習時に提出せよ。
ただし、reportルーチンの出力は外さないこと。
averageルーチンの出力は外しても良い。
* 授業アンケート [#i726de10]
これで全2回の「Fortran90/95入門」は終わりです。
全体的に難易度はどうでしたか?
#vote(簡単すぎた[0], 難しすぎた[0], ちょうどよかった[11])
C言語との比較による説明の仕方について
#vote(Cとの比較で良い[6], CよりもJavaと比較して欲しい[1], 他の言語との比較はしない方がわかりやすい[1])
------------------------------
as of &_now; (&counter;)