前回までの補足
「実行文」と「非実行文」
Fortran プログラムにおける「文」は、「実行文」と「非実行文」の2つに大別される。
- 実行文
- プログラムの実行手順を詳しく記述する文のこと。 計算結果を代入したり、データの読み書きをしたりする文がその例。
- 非実行文
- プログラムの実行時に必要な情報を記述する文のこと。 変数の名前や型を宣言したり、プログラムの名前を宣言したりする文がその例。
非実行文に関して特に注意すべき規則として、変数を宣言する文は最初の実行文より前に記述しておかなければならない ことを覚えておこう。 これが守られていないと、コンパイル時にエラーになる (gfortran に怒られる)。
計算の流れを制御するしくみ
この演習の最初に作ったプログラム を例にとって説明すると、
-
最初の do 文と end do 文で囲まれた部分がひたすら繰り返される (「do ループ」という)。 ただしこの do ループは、2回生後期の実験で習ったものとは違い、出発値や終了値などがついていない ことに注意。 この do ループは、特に何も問題が起こらなければ 無限に回り続ける (「無限ループ」) ので、ループを抜ける条件を別に指定 してやる必要がある (説明は ここ) 。
-
read 文でデータを読む。 read 文は入力されたデータを1行ずつ読もうとする 仕組みになっており、1行分のデータを読んだらプログラム内の次の処理に移動する。 またここでは iostat=joutai という指定をしてあることにより、入力の状態が joutai という整数型変数の値で判定できる (説明は ここ) 。
- もしデータが正しく読めたら、read 文は joutai に 0 を入れて返す。
- もし読むデータがなくなっていた (より正確には「データが正しく読めなかった」) ら、read 文は joutai に 0 以外の値を入れて返す。
-
if 文でループ内の処理を続けるかどうかを判定する。
- joutai が 0 の場合にはループ内の処理を続け、処理が終わったらループの先頭に戻る。
- joutai が 0 以外の場合にはループから抜ける。
今日のテーマ: 平均・分散・標準偏差 (その2)
今日 (とその次の回) の目標は「1890年から2023年までの134年間 の、1月〜12月までの各月の気温の平均・分散・標準偏差 を求め、それを表示させる」こと。
今回の課題にとり組むにあたり、必要な手順は
- 各月の気温の総和を求める (ただし分散・標準偏差を出すときには気温の2乗の総和もいる)
- 何年分のデータを読み込んだかを数える
- 平均・分散・標準偏差を求める
とはいえ、(過去数年間の演習でもそうだったのだが) 12ヶ月分を一気に計算するプログラムをいきなり作ると混乱するであろうから、まずはある月の気温の平均値だけに注目したプログラムを作ってみることから始めよう。
準備: 4月の気温の平均値を求める
手始めに、4月の気温の過去134年間の平均を計算するプログラムを作成してみよう。 このプログラムのミソは以下の通り。
- 4月の気温の総和を求める
基本的な考え方は、前回 の「方法その2」で作ったプログラムと同様。
「4月の気温の総和」を入れる器を用意する。 例えば kion04_souwa という名前で、実数を入れる変数を宣言する。
「4月の気温の総和」を入れる器の中身をあらかじめ「空っぽ」にしておく。
「4月の気温の総和」を入れる器にどんどん値を足していく。 その前の read 文によって 1行分のデータ (要するに「ある年の各月の気温」) を読むたびに、kion04_souwa という器の中に、その年の4月の平均気温の値を足し込んでいる。
- データを数える
今回の「各月の気温の平均をとる」課題と、前回にやった「各年の平均気温を求める」課題との大きな違いは、平均をとるのに使うデータの個数があらかじめ分かっているとは限らない こと。 その場合に有効な考え方は、いくつのデータを読み込んだかをプログラムに数えさせてやる ことである。 そのためには、次のような手順を行えばよい。 なおこの手順は 気温の総和を求める手順と非常によく似ている ことに注意。
「読み込んだデータの年数」を入れる器を用意する。 例えば nensuu という名前で、整数を入れる変数 を宣言する。
「読み込んだデータの年数」を入れる器の中身をあらかじめ「空っぽ」にしておく。
「読み込んだデータの年数」を入れる器にどんどん値を足していく。 ここでは、1行読むたびに nensuu を1ずつ増やす ようにしている。
「4月の平均気温」を計算する。 その際、「読み込んだデータの数で割る」操作は /nensuu ではなく /real(nensuu) と書くほうが安全 である。 ここで real(nensuu) は 整数 nensuu の値を実数だと思うことにする ための命令である。 詳しい説明は後日。
「4月の平均気温」を表示させる。 この例では、最後の write 文で、読み込んだ行の総数 nensuu と4月の平均気温 kion04_heikin を出力 している。
p | r | o | g | r | a | m | e | n | s | h | u | 0 | 3 | a | |||||||||||||||||||||||||||||||||||||||||||||||||
i | m | p | l | i | c | i | t | n | o | n | e | ||||||||||||||||||||||||||||||||||||||||||||||||||||
i | n | t | e | g | e | r | : | : | s | e | i | r | e | k | i | , | j | o | u | t | a | i | |||||||||||||||||||||||||||||||||||||||||
r | e | a | l | , | d | i | m | e | n | s | i | o | n | ( | 1 | 2 | ) | : | : | k | i | o | n | ||||||||||||||||||||||||||||||||||||||||
r | e | a | l | : | : | k | i | o | n | _ | n | e | n | _ | h | e | i | k | i | n | , | k | i | o | n | _ | n | e | n | _ | s | o | u | w | a | ||||||||||||||||||||||||||||
r | e | a | l | : | : | k | i | o | n | _ | n | e | n | _ | b | u | n | s | a | n | , | k | i | o | n | _ | n | e | n | _ | h | e | n | s | a | ||||||||||||||||||||||||||||
r | e | a | l | : | : | k | i | o | n | 2 | _ | n | e | n | _ | s | o | u | w | a | , | k | i | o | n | 2 | _ | n | e | n | _ | h | e | i | k | i | n | ||||||||||||||||||||||||||
r | e | a | l | : | : | k | i | o | n | 0 | 4 | _ | h | e | i | k | i | n | , | k | i | o | n | 0 | 4 | _ | s | o | u | w | a | ||||||||||||||||||||||||||||||||
i | n | t | e | g | e | r | : | : | i | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
i | n | t | e | g | e | r | : | : | n | e | n | s | u | u | |||||||||||||||||||||||||||||||||||||||||||||||||
k | i | o | n | 0 | 4 | _ | s | o | u | w | a | = | 0 | . | 0 | ||||||||||||||||||||||||||||||||||||||||||||||||
n | e | n | s | u | u | = | 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
d | o | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
r | e | a | d | ( | * | , | ' | ( | i | 4 | , | 1 | 2 | f | 5 | . | 1 | ) | ' | , | i | o | s | t | a | t | = | j | o | u | t | a | i | ) | s | e | i | r | e | k | i | , | k | i | o | n | |||||||||||||||||
i | f | ( | j | o | u | t | a | i | / | = | 0 | ) | e | x | i | t | |||||||||||||||||||||||||||||||||||||||||||||||
n | e | n | s | u | u | = | n | e | n | s | u | u | + | 1 | |||||||||||||||||||||||||||||||||||||||||||||||||
k | i | o | n | 0 | 4 | _ | s | o | u | w | a | = | k | i | o | n | 0 | 4 | _ | s | o | u | w | a | + | k | i | o | n | ( | 4 | ) | |||||||||||||||||||||||||||||||
k | i | o | n | _ | n | e | n | _ | s | o | u | w | a | = | 0 | . | 0 | ||||||||||||||||||||||||||||||||||||||||||||||
k | i | o | n | 2 | _ | n | e | n | _ | s | o | u | w | a | = | 0 | . | 0 | |||||||||||||||||||||||||||||||||||||||||||||
d | o | i | = | 1 | , | 1 | 2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
k | i | o | n | _ | n | e | n | _ | s | o | u | w | a | = | k | i | o | n | _ | n | e | n | _ | s | o | u | w | a | + | k | i | o | n | ( | i | ) | |||||||||||||||||||||||||||
k | i | o | n | 2 | _ | n | e | n | _ | s | o | u | w | a | = | k | i | o | n | 2 | _ | n | e | n | _ | s | o | u | w | a | + | k | i | o | n | ( | i | ) | * | * | 2 | ||||||||||||||||||||||
e | n | d | d | o | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
k | i | o | n | _ | n | e | n | _ | h | e | i | k | i | n | = | k | i | o | n | _ | n | e | n | _ | s | o | u | w | a | / | 1 | 2 | . | 0 | |||||||||||||||||||||||||||||
k | i | o | n | 2 | _ | n | e | n | _ | h | e | i | k | i | n | = | k | i | o | n | 2 | _ | n | e | n | _ | s | o | u | w | a | / | 1 | 2 | . | 0 | |||||||||||||||||||||||||||
k | i | o | n | _ | n | e | n | _ | b | u | n | s | a | n | = | k | i | o | n | 2 | _ | n | e | n | _ | h | e | i | k | i | n | - | k | i | o | n | _ | n | e | n | _ | h | e | i | k | i | n | * | * | 2 | |||||||||||||
k | i | o | n | _ | n | e | n | _ | h | e | n | s | a | = | s | q | r | t | ( | k | i | o | n | _ | n | e | n | _ | b | u | n | s | a | n | ) | ||||||||||||||||||||||||||||
w | r | i | t | e | ( | * | , | ' | ( | i | 4 | , | 1 | 3 | f | 6 | . | 1 | , | 2 | f | 6 | . | 2 | ) | ' | ) | s | e | i | r | e | k | i | , | k | i | o | n | , | k | i | o | n | _ | n | e | n | _ | h | e | i | k | i | n | & | |||||||
, | k | i | o | n | _ | n | e | n | _ | b | u | n | s | a | n | , | k | i | o | n | _ | n | e | n | _ | h | e | n | s | a | |||||||||||||||||||||||||||||||||
e | n | d | d | o | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
k | i | o | n | 0 | 4 | _ | h | e | i | k | i | n | = | k | i | o | n | 0 | 4 | _ | s | o | u | w | a | / | r | e | a | l | ( | n | e | n | s | u | u | ) | |||||||||||||||||||||||||
w | r | i | t | e | ( | * | , | ' | ( | i | 4 | , | 1 | 2 | f | 6 | . | 2 | ) | ' | ) | n | e | n | s | u | u | , | k | i | o | n | 0 | 4 | _ | h | e | i | k | i | n | ||||||||||||||||||||||
e | n | d | p | r | o | g | r | a | m | e | n | s | h | u | 0 | 3 | a |
前回作ったプログラム (enshu02b.f90 または enshu02c.f90) を出発点にして、上のプログラムを (例えば enshu03a.f90 という名前で) 作成せよ。 入力ができたら、例の如く
$ gfortran enshu03a.f90と実行してみよ。 プログラムに間違いがなかったら、端末には以下のように出力されているはずだ (最後の4行分のみ示す)。
$ cat matsuyama-kion.txt | ./a.out
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 13.72
各月の気温の平均値を一気に求める
次はいよいよ、12ヶ月分を一気に計算するプログラムを作ってみよう。 上で作ったプログラム enshu03a.f90 の 適切な場所に、以下に示す「部品」をうまく追加する ことで目的のプログラム enshu03b.f90 が完成するようになっている。 ただし、どこに追加するのが適切かは 各自で考える こと。 なお置き場所の正解は一通りではない。 プログラミングにはよくあることなのだが、(極端にいうと) プログラムが正しく動きさえすれば何でも OK なのである。
必要な手順 (の要点だけ) を以下に示す。
「各月の気温の総和」と「各月の平均気温」を入れる器を用意する。
4月の気温だけを考えた例とは違って、今度は「総和」用の器と「平均」用の器をそれぞれ12ヶ月分用意してやる必要がある。 だからといって、kion01_souwa から kion12_souwa まで、また kion01_heikin から kion12_heikin までの実数用の器を合計24個も用意するなんてやりたくないはず。 そんな時こそ (再び) 配列 の出番である。
例えば kion_tsuki_souwa と kion_tsuki_heikin という名前の、12個の実数を入れる配列 を宣言する。r e a l , d i m e n s i o n ( 1 2 ) : : k i o n _ t s u k i _ s o u w a , k i o n _ t s u k i _ h e i k i n 「各月の気温の総和」を計算する。 例の如く、容器の中身をあらかじめ「空っぽ」にし、do ループ内でデータを読むたびに容器の中に足し込んでいけばいいのだが、その手順を例えば
からk i o n _ t s u k i _ s o u w a ( 1 ) = 0 . 0
までの12回書くとか、はたまたk i o n _ t s u k i _ s o u w a ( 1 2 ) = 0 . 0
からk i o n _ t s u k i _ s o u w a ( 1 ) = k i o n _ t s u k i _ s o u w a ( 1 ) + k i o n ( 1 )
などと延々と書くのは、余りにも面倒だし残念だ。 こういう時こそ do ループ の出番である。k i o n _ t s u k i _ s o u w a ( 1 2 ) = k i o n _ t s u k i _ s o u w a ( 1 2 ) + k i o n ( 1 2 ) 各月の気温の平均を計算する。 まさかここにも
からk i o n _ t s u k i _ h e i k i n ( 1 ) = k i o n _ t s u k i _ s o u w a ( 1 ) / r e a l ( n e n s u u )
まで延々書くなんてことはやりたくないはず。k i o n _ t s u k i _ h e i k i n ( 1 2 ) = k i o n _ t s u k i _ s o u w a ( 1 2 ) / r e a l ( n e n s u u ) 各月の気温の平均を表示する。 最後に結果を出力する箇所は以下のようにしておくこと。 この例では、読み込んだ行の総数 nensuu と各月の平均気温 kion_tsuki_heikin を出力 している。 さらに最後の write 文では出力の書式を少し変更し、平均気温は小数点以下を2桁表示 するようにした。 詳しい説明は ここ を参照のこと。
w r i t e ( * , ' ( i 4 , 1 3 f 6 . 2 ) ' ) n e n s u u , k i o n _ t s u k i _ h e i k i n
これらを参考にして、各月の気温の平均を計算する命令がどう書けるかを考えよ。 そして、今日の最初に作ったプログラム enshu03a.f90 にその機能を追加せよ [みほん]。 なお「完成までの途中経過」あるいは「悪い見本」として、
- (12ヶ月分を求める準備はしてあるのに) 4月の平均気温だけを求めるプログラム例を ここに
- (データの読み込みに必要なものを除いた) 全ての do ループを (わざと) 外した プログラム例を ここ に
なお、正しく計算できていれば、プログラムの出力は以下のようになるはずである。
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正しく動いていることが確認できたら、本日の演習の出席確認のため、このプログラムを 末尾のフォーム 経由で提出せよ。
動作確認「手抜き」版
ここでももちろん、自分の作ったプログラムが平均気温を正しく計算できているかを確認するのが大事だが、「134個のデータの平均値を電卓で計算する」なんてことはさすがにやる気にならない。 そこで、Linux の別のコマンドを少々使って、「手抜き」した動作確認をしてみよう。
そのための準備として、以下のようにしてみよう ($ はコマンドプロンプト)。
$ tail matsuyama-kion.txtこれにより、最近10年間のデータだけ (より正確には、「matsuyama-kion.txt の最後の10行だけ」) が表示されるはずだ。 この tail とは、「そのファイルの最後の10行だけを表示せよ」という Linux のコマンドである。 詳しくは Linux の教科書や
$ man tailとして表示されるオンラインマニュアルを参照のこと。 また tail コマンドをうまく使ってやると、「最後の10行だけを表示」する代わりに、例えば「最後の3行だけを表示」させることも可能である。 tail コマンドのオンラインマニュアルを読み、そのための方法を考えてみよ。
では、いよいよ自分の作ったプログラムが正しく動いているかを確認しよう。
$ tail -n 3 matsuyama-kion.txt | ./a.outとすると、最近の3年間だけのデータを読み込んで計算させることができる。 この出力をもとに、自分の作ったプログラムが正しく平均値を計算できているかを確認せよ。
なお、最近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
ちなみに、tail とよく似た Linux コマンドで、「そのファイルの先頭の n 行だけを表示せよ」というコマンド head もある。 このコマンドを使って、「1890年から3年間の平均気温」も計算させてみよ。
余力のある人向けのおまけ: Fortran 90 の「配列処理」機能をとことん使った「手抜き」
上のプログラム中にもあるような、配列の中身全てを操作する命令は、Fortran 90 の機能を使えば もっと簡略な書き方が可能 である。 例えば、
12個の要素をもつ配列 kion_tsuki_souwa の中身を全て空っぽにしておくという操作は
とかk i o n _ t s u k i _ s o u w a ( 1 : 1 2 ) = 0 . 0
あるいはもっと簡単にk i o n _ t s u k i _ s o u w a ( : ) = 0 . 0
と書ける。k i o n _ t s u k i _ s o u w a = 0 . 0 -
同様に、12個の要素をもつ配列 kion_tsuki_souwa に、同じく12個の要素をもつ配列 kion の中身をそれぞれ追加するという操作は
とかk i o n _ t s u k i _ s o u w a ( 1 : 1 2 ) = k i o n _ t s u k i _ s o u w a ( 1 : 1 2 ) + k i o n ( 1 : 1 2 )
あるいはもっと簡単にk i o n _ t s u k i _ s o u w a ( : ) = k i o n _ t s u k i _ s o u w a ( : ) + k i o n ( : )
と書くこともできる。k i o n _ t s u k i _ s o u w a = k i o n _ t s u k i _ s o u w a + k i o n
以下、個人的な見解だが、あまりこのような 「手抜き」記法は乱発すべきではない と思う。 その理由は、こうした記法では 「配列のどの要素を操作しているか」が分かりにくくなる し、特に配列の名前だけしか書かない記法では 一見しただけでは配列か否かの見分けがつかない からである。 さらに 式中の各項に現われる配列の要素数が異なっているとエラーになる という面倒臭さもある。 そのため、こうした記法を使うのはごく限られた場合 (例えば配列の全ての要素にゼロを入れたい場合など) だけに留めるのがいいと思う。
簡単なグラフを描く
前回にも少しやった通り、Linux 上で動く簡単なグラフ化ツールである Gnuplot を使って、計算で求まった「平均」の意味をグラフで確認してみよう。
まずは端末で以下のように入力してみよう ($ は端末のコマンドプロンプト)。
$ gnuplotすると Gnuplot の入力画面に入る。 そこで
gnuplot> plot "matsuyama-kion.txt" using 1:5 with lines, 13.72とし、表示されたグラフの意味を考えてみよ。 その際、各年の4月の気温を表す折れ線グラフと、その平均値を表すグラフの位置関係に注目せよ。 また同様の作業を1月から12月までの全ての月についても行い、「平均値」のもつ意味を確認せよ。
出席確認用プログラム提出フォーム
Moodle のコースページ 以下の 課題ファイル提出 により、各自で作成した (enshu03b.f90 に相当する) 過去134年間の各月の平均気温を計算する Fortran 90 プログラムの最終版を提出せよ。