情報地球科学演習 (2024年前学期) 第04回

前回までの補足

「コメント書き」のススメ

既に2回生後期の「地球物理学実験」で習った通り、!のある行では、gfortran は 「!」以降を読み飛ばしてくれる。 これを利用して、Fortran のプログラムに (例えば) 「こういう式で求めればOK」とか「ここは○○を求めようとしているところ」などといった 注釈を入れる ことができる。 自分の作ったプログラムにはなるべく注釈をつけておくとよい。

ただし、(できるだけ) 注釈は「半角文字」だけを使って書く ようにしておくほうがよい。 注釈でないところに全角文字が残っていると gfortran に怒られてしまう。 特に 「全角スペース」の消し忘れ は気付きにくいので要注意。

変数の「型」と演算

前回 の演習では、平均を計算する際に出てくる「総和を整数 nensuuで割る」操作は /nensuu ではなく /real(nensuu) と書くほうが安全 だと説明した。 では、「安全でない」操作をするとどんな事故が起こり得るのだろうか。

例えば以下のプログラムでは、a の値と b の値が同じになるという保証はない し、同様に c の値と d の値が同じになるという保証もない。 実際に以下のプログラム (例えば kata.f90 などというファイル名にしておく) を入力・コンパイル・実行してみることにより、a から d の値がいくらになったかを調べてみよ。 またそうなった理由を考えてみよ。 なお、コンパイル・実行は

$ gfortran kata.f90
$ ./a.out
とすればOK。 あるいは、「もうちょっと Linux に慣れてみたい」という気分になってきた人は、2年生後期の「地球物理学実験」の中でも出てきた内容を思い出して
$ gfortran -o kata.exe kata.f90
$ ./kata.exe
としても同じ結果が得られる。 ちなみに出力は以下のようになるはずである。

   3.00000000       3.50000000       3.00000000       3.50000000    
program kata
  implicit none
  integer :: i1,i2
  real :: a,b,c,d
  i1 = 7
  i2 = 2
  a = i1/i2
  b = real(i1)/real(i2)
  c = 7/2
  d = 7.0/2.0
  write(*,*)a,b,c,d
end program kata

上の例からも分かるように、代入文の右辺と左辺で現われる変数の「型」が一致していない と、予期しない計算結果が返ってくることもあり得る。 不測の事態を避けるためには、プログラムを書く本人が、変数の型を常に意識した上で計算式を書く ように心掛けるのが最も確実な方法である。

Fortran 90 の「配列処理」機能: 「便利さ」とその「罠」

前回 作成したプログラム中にもあるような、配列の中身 (の全て) を操作する命令は、Fortran 90 の機能を使うともっと簡略な書き方が可能である。 例えば、

この機能は古い規格の Fortran (Fortran 77 とか) ではそもそも用いることができなかったものである。

以下、個人的な見解だが、あまりこのような 「手抜き」記法は乱発すべきではない と思う。 その理由は、こうした記法では 「配列のどの要素を操作しているか」が分かりにくくなる し、特に配列の名前だけしか書かない記法では 一見しただけでは配列か否かの見分けがつかない からである。 さらに 式中の各項に現われる配列の要素数が異なっているとエラーになる という面倒臭さもある。 そのため、こうした記法を使うのはごく限られた場合 (例えば配列の全ての要素にゼロを入れたい場合など) だけに留めるのがいいと思う。

さらにいえば、 (プログラミング以外でもそうだろうが) 便利なものほど、十分気をつけて使わないと危険 である。 過去にも実際、 この「配列処理」機能だと gfortran に (間違って) 解釈されてしまうという「罠」に捕まっている例が見られた。 その「罠」の「仕掛け」は、次に示す2つのプログラムの動作の違いから理解できる。 これら2つのプログラムをそれぞれコンパイル・実行した際の出力は異なるのだが、その理由を考えてみよ。

プログラムその0プログラムその1
program hairetsu0                 
implicitnone
real,dimension(12)::kion
integer::i
doi=1,12
kion(i)=0.0
enddo
doi=1,12
kion(i)=real(i)
write(*,'(i2,12f5.1)')i,kion
enddo
endprogramhairetsu0
program hairetsu1                 
implicitnone
real,dimension(12)::kion
integer::i
doi=1,12
kion(i)=0.0
enddo
doi=1,12
kion=real(i)
write(*,'(i2,12f5.1)')i,kion
enddo
endprogramhairetsu1
 1  1.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 2  1.0  2.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 3  1.0  2.0  3.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 4  1.0  2.0  3.0  4.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 5  1.0  2.0  3.0  4.0  5.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 6  1.0  2.0  3.0  4.0  5.0  6.0  0.0  0.0  0.0  0.0  0.0  0.0
 7  1.0  2.0  3.0  4.0  5.0  6.0  7.0  0.0  0.0  0.0  0.0  0.0
 8  1.0  2.0  3.0  4.0  5.0  6.0  7.0  8.0  0.0  0.0  0.0  0.0
 9  1.0  2.0  3.0  4.0  5.0  6.0  7.0  8.0  9.0  0.0  0.0  0.0
10  1.0  2.0  3.0  4.0  5.0  6.0  7.0  8.0  9.0 10.0  0.0  0.0
11  1.0  2.0  3.0  4.0  5.0  6.0  7.0  8.0  9.0 10.0 11.0  0.0
12  1.0  2.0  3.0  4.0  5.0  6.0  7.0  8.0  9.0 10.0 11.0 12.0
 1  1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0
 2  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 3  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0
 4  4.0  4.0  4.0  4.0  4.0  4.0  4.0  4.0  4.0  4.0  4.0  4.0
 5  5.0  5.0  5.0  5.0  5.0  5.0  5.0  5.0  5.0  5.0  5.0  5.0
 6  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
 7  7.0  7.0  7.0  7.0  7.0  7.0  7.0  7.0  7.0  7.0  7.0  7.0
 8  8.0  8.0  8.0  8.0  8.0  8.0  8.0  8.0  8.0  8.0  8.0  8.0
 9  9.0  9.0  9.0  9.0  9.0  9.0  9.0  9.0  9.0  9.0  9.0  9.0
10 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0
11 11.0 11.0 11.0 11.0 11.0 11.0 11.0 11.0 11.0 11.0 11.0 11.0
12 12.0 12.0 12.0 12.0 12.0 12.0 12.0 12.0 12.0 12.0 12.0 12.0

gfortranとうまくつき合うためのコツを少々、あるいは「デバッグ道」入門

(最近はプログラム以外についても使われているようだけど) プログラム中の誤りや欠陥を「バグ (bug; 成虫のこと)」といい、バグを除去する作業を「デバッグ (debug)」という。 どんなプログラミングでもだいたいそうなのだが、実際にタイピングしている時間は作業時間の3割くらいで、それ以外にあたる 作業時間の7割はデバッグに費される ものである。 言い換えれば、たとえ プロのプログラマであっても、一発で gfortran が通らないのが当たり前 なのである。

だから要するに gfortran に怒られたからといって嘆き悲しむ必要など全くない のだが、とはいっても gfortran にしょっちゅう怒られていると気分も滅入ってくるであろう。 例えば以下のような対策をとれば、gfortran とも (少しは) うまくつき合ってもらえるのではないか (と思う)。

Fortran 言語に限らず、プログラムのデバッグを進めるコツの1つは、コンパイラをうまく利用することである。 gfortran にバグを見つけてもらう」 という心構え (開き直り? 割り切り?) ができたとすれば、この先の「デバッグ道」もスムーズに進めるはずだ。

さらに (ちょっと高度な話になるが)、gfortran が指摘できる程度のバグなんてのは、容易に発見できるので、恐ろしくも何ともない。 実は本当に恐ろしいのは、gfortran に見逃がされてしまうバグ であり、上で紹介した「配列処理」機能の「罠」はその例の1つである。 こうしたバグを見つけるには、もうちょっと別のコツがある (けど、紹介はまたの機会に)。

今日のテーマ: 平均・分散・標準偏差 (その3)

今日の目標は「1月〜12月までの各月の気温の平均・分散・標準偏差を求め、それを表示させる」こと。 前回 の演習にて、「1890年から2023年までの134年間 の、1月〜12月までの各月の気温の平均」を求めるプログラムの作成は終わっているはずだが、復習を兼ねて、プログラムの中身とその動作の確認を行ってみる。

前回に作ったプログラムの「みほん」

復習の便利のために、前回作成した「平均」を計算するプログラムの「みほん」を公開します (印刷したい人は PDF版 を)。 この「みほん」と自分の書いたプログラムを比較することにより、

  1. Fortran 90 プログラムの規則
    • implicit none は「お約束」として必ずつけておく (理由を知りたい人は ここ を)。
    • 変数を宣言する文 (「非実行文」の一種) は、最初の「実行文」が現れる前に書く。
    • 1行の長さは最大で 132文字 まで。 行の最後にある「&」記号は、次の行へと継続する印。 もしこの「1行132文字」制限を超えそうになったら、行の最後に「&」記号を置いて次の行を起こす。 (詳しい説明は ここ を)
  2. 「配列」とは (詳しい説明は ここ を)
    • いくつもの「変数」をまとめて作った1つの「かたまり」
    • 配列の「要素」の参照のしかた (「番地」を指定する)
  3. do ループによる処理の繰り返し (詳しい説明は ここ を)
  4. if 文 (条件分岐) (詳しい説明は ここ とか ここ を)
  5. 「総和」のとり方、データの数え方
  6. 平均をとるための手順の「分解」
    • 「平均」をとるには「データの総和」と「データの数」が必要
をしっかり復習しておくこと。

既にプログラムが正しく作成できている (はずの) 人も、念のため再び

$ gfortran enshu03b.f90
$ cat matsuyama-kion.txt | ./a.out
などと実行し、過去134年分の各月の気温の平均の値が正しく求まっているかをもう一度確認すること。 正しく計算できていれば、プログラムの出力は以下のようになるはずである (最後の4行のみ示す)。

2021   6.2   8.9  12.7  15.5  19.5  23.4  27.2  27.5  25.0  20.1  13.7   8.8  17.4 51.21  7.16
2022   5.9   5.2  11.9  15.9  19.3  24.1  27.8  29.1  26.2  19.2  15.5   7.4  17.3 65.94  8.12
2023   6.5   7.6  12.7  15.9  19.8  23.1  28.0  28.9  27.3  19.4  14.8   9.3  17.8 57.90  7.61
 134  5.26  5.59  8.64 13.72 17.97 21.91 26.24 27.05 23.48 17.69 12.51  7.68

平均のほかに分散と標準偏差も求める

次はいよいよ、分散と標準偏差も計算する機能を追加してみよう。 上で作ったプログラム enshu03b.f90適切な場所に、以下に示す「部品」をうまく追加する ことで目的のプログラム enshu03c.f90 が完成するようになっている。 ただし、どこに追加するのが適切かは 各自で考える こと。

新たに追加すべき重要な機能は、各月の気温の2乗の平均値を計算する ことと、分散の平方根をとって標準偏差を計算する ことだけ。 そのための機能は、例えばそれぞれ以下のようになるはずだ。

気温の平均値を計算する際に追加した機能を思い出し、各月の気温の分散や標準偏差を計算する命令がどう書けるかを考えよ。 そして、その命令を追加したプログラムを作成せよ [みほん]。 またもちろん、自分の作ったプログラムが (平均+) 分散・標準偏差を正しく計算できているかを (少なくとも「手抜き」版で) 確認せよ。 なおプログラムに間違いがなかったら、

$ tail -n 3 matsuyama-kion.txt | ./a.out
として、過去3年分のデータだけを処理した場合、出力は以下のようになるはずである。
2021   6.2   8.9  12.7  15.5  19.5  23.4  27.2  27.5  25.0  20.1  13.7   8.8  17.4 51.21  7.16
2022   5.9   5.2  11.9  15.9  19.3  24.1  27.8  29.1  26.2  19.2  15.5   7.4  17.3 65.94  8.12
2023   6.5   7.6  12.7  15.9  19.8  23.1  28.0  28.9  27.3  19.4  14.8   9.3  17.8 57.90  7.61
   3  6.20  7.23 12.43 15.77 19.53 23.53 27.67 28.50 26.17 19.57 14.67  8.50
   3  0.06  2.35  0.14  0.04  0.04  0.18  0.12  0.51  0.88  0.15  0.55  0.65
   3  0.24  1.53  0.38  0.19  0.21  0.42  0.34  0.71  0.94  0.39  0.74  0.80

最後に、matsuyama-kion.txt に含まれている全てのデータ (1890年から2023年までの各月の平均気温) を読み込ませ、この期間の各月の平均気温、及びその分散と標準偏差を計算せよ。 標準偏差の最も大きい (平均気温のばらつきが最大) のは何月か。 また最も小さいのは何月か。

なお、正しく計算できていれば、プログラムの出力 (の最後の3行) は以下のようになるはずである。

 134  5.26  5.59  8.64 13.72 17.97 21.91 26.24 27.05 23.48 17.69 12.51  7.68
 134  1.51  2.10  2.00  1.52  1.33  0.98  1.37  1.09  1.74  1.53  1.60  1.53
 134  1.23  1.45  1.42  1.23  1.15  0.99  1.17  1.04  1.32  1.24  1.27  1.24
正しく動いていることが確認できたら、本日の演習の出席確認のため、このプログラムを 末尾のフォーム 経由で提出せよ。

簡単なグラフを描く

前回にも少しやった通り、Linux 上で動く簡単なグラフ化ツールである Gnuplot を使って、計算で求まった「平均」や「標準偏差」の意味をグラフで確認してみよう。

まずは端末で以下のように入力してみよう ($ は端末のコマンドプロンプト)。

$ gnuplot
すると Gnuplot の入力画面に入る。 そこで
gnuplot> plot "matsuyama-kion.txt" using 1:5 with lines, 13.72
とし、表示されたグラフの意味を考えてみよ。 その際、各年の4月の気温を表す折れ線グラフと、その平均値を表すグラフの位置関係に注目せよ。 また同様の作業を1月から12月までの全ての月についても行い、「平均値」のもつ意味を確認せよ。 また、自分で計算して得られた4月の気温の標準偏差 (答は 1.23 になるはず) の値を用いて
gnuplot> plot "matsuyama-kion.txt" using 1:5, 13.72, 13.72+1.23, 13.72-1.23
(この図は折れ線グラフで描かないほうが見やすい) とし、「標準偏差」のもつ意味を確認せよ。 さらに、
gnuplot> plot "matsuyama-kion.txt" using 1:5, 13.72, 13.72+1.23*2, 13.72-1.23*2
gnuplot> plot "matsuyama-kion.txt" using 1:5, 13.72, 13.72+1.23*3, 13.72-1.23*3
として得られるグラフと先のグラフを比較し、違いを考えてみよ。

出席確認用プログラム提出フォーム

Moodle のコースページ 以下の 課題ファイル提出 により、各自で作成した (enshu03c.f90 に相当する) 過去134年間の各月の気温の平均・分散・標準偏差を計算する Fortran 90 プログラムの最終版を提出せよ。