5. 繰り返しと出力の書式 目次へ 5.1. 決まった回数の単純な繰り返し 5.1.1. 多重ループ 5.2. 出力の書式指定 5.2.1. 書式の構成要素 5.3. 途中で抜ける 5.3.1. EXIT文とCYCLE文 [90] 5.3.2. DO WHILE文 [90] 5.4. DO型出力リスト 5.1. 決まった回数の単純な繰り返し 例題 5_1「1から100までの整数の2乗の和を計算する。」[ex5_1.f90] ! --- Square Sum from 1 to 100 --- [90] 方式 INTEGER :: n, sum = 0 ! 宣言文で初期値を設定できる。 DO n = 1, 100 ! END DO までの間を繰り返す。 sum = sum + n*n END DO PRINT*, sum END C --- Alternative --- 従来方式 INTEGER n, sum/0/ ← 初期値設定の書き方が異なる。 DO 10 n = 1, 100 ← 文番号10の文までの間を繰り返す。 sum = sum + n*n 10 CONTINUE ← CONTINUE は「何もしない」実行文 PRINT*, sum END
(DOループ原形) DO DO制御整数=初期値,終値 [,増分値] [90] .......................... END DO [ラベル] ←DO文にラベルを付けたとき にはラベルを省略できない。 従来方式 DO 文番号 DO制御整数=初期値,終値[,増分値] .......................... 文番号 終端実行文 ← 例 CONTINUE文 (1) 増分値は省略すると1となる。 (2) 繰り返すごとに制御整数を増分値ずつ増やし,終値を越えた時点でループを 抜ける。 (3) したがって,ループの最後まで繰返して出た時点では,制御整数は終値よりも (最大で増分値だけ)大きい。(上の例では n=101 となって抜けている。) (4) 初期値が終値を越えている場合には,一度もループを実行しない。 (5) ループの途中へ外から飛び込んではならない。 (6) したがって,ループは何重になってもよいが交差してはならない。 5.1.1.多重ループ ループは交差してさえいなければ,何重になってもよい。多重ループでは,外側の ループの制御変数が変わるごとに内部のループを設定回数だけ繰り返す,という形で 実行される。例えば次の例では「二一が2,二二ンが4,二三が6,二四が8,..., 二九18,三一が3,三二が6,...」と進行する。 例題 5_2「九九の練習プログラム」[ex5_2.f90] !----- 九九 ------ INTEGER :: m, n, mn var1: DO m = 2, 9 var2: DO n = 1, 9 PRINT*, m, ' かける', n, '?' READ*, mn IF(mn == m*n) THEN PRINT*,'正解です。' ELSE PRINT*,'誤りです。' END IF END DO var2 END DO var1 END
プログラムは,対応する DO文 と END DO文 の頭を上下に揃えて書くようにデザイン すればわかりやすい。DO文 と END DO文 の間が長くて一目で見ても対応が分からない ようなときには,対応を明確にするため ラベル名 を付けて識別すればよい。 5.2. 出力の書式指定 例題 5_3「0度から90度まで10度刻みで,小数点以下5桁の精度の三角関数 SIN と COS の数表を作れ。」[ex5_3.f90] ! --- Table of SIN and COS --- REAL, PARAMETER:: RAD = 3.1415926/180.0 REAL :: r, s, c ; INTEGER :: k PRINT*, "角度 SIN COS CHECK" DO k = 0, 90, 10 r = k*RAD; s = SIN(r); c = COS(r) PRINT '(1X, I3, 2X, 3F10.5)', k, s, c, s**2+c**2 END DO END
(実行結果) 角度 SIN COS CHECK 0 0.00000 1.00000 1.00000 10 0.17365 0.98481 1.00000 20 0.34202 0.93969 1.00000 30 0.50000 0.86603 1.00000 40 0.64279 0.76604 1.00000 50 0.76604 0.64279 1.00000 60 0.86603 0.50000 1.00000 70 0.93969 0.34202 1.00000 80 0.98481 0.17365 1.00000 90 1.00000 0.00000 1.00000 上のプログラムのPRINT文の'( )'の中の記号の意味: 1X 先頭に一つ空白をあける。(注) I3 3文字幅に整数の値を書く。 2X 空白を2つあける。 3F10.5 3つの実数の値を,それぞれ10文字幅に小数点以下5桁の精度で書く。 注)「先頭の1文字はプリンタの制御に予約されているため印字されない。」という FORTRANの歴史が引き継がれているシステムがあるため。 (書式の指定) 今まで PRINT* の*(デフォルト仕様)を書いてきた場所に書式を指定する: 直接指定する方法 PRINT 書式識別子,出力データのリスト または WRITE(*, 書式識別子)出力データのリスト 書式識別子:*印,書式を表す文字定数または文字変数 FORMAT文の方法 PRINT 文番号,出力データのリスト または WRITE(*, 文番号)出力データのリスト 文番号 FORMAT(*ではない書式識別子の内容) 注)整数変数で使用するFORMAT文の文番号を指定することはできない。 (不可の例) n = 100 PRINT n, ... ............ 100 FORMAT(...) 例 例題5_2のPRINT文は以下のように書くことができる: PRINT 100, k, s, c, s**2+c**2 100 FORMAT(1X, I3, 2X, 3F10.5) 注)WRITE文の*印は,7章で「入力データファイル指定」をする場所である。*は コンソール入力(キーボード入力)を示す。 5.2.1. 書式の構成要素 ⇒詳しくは付録Bへ 以下の要素(編集記述子)をコンマ「,」または斜線「/」で区切って並べたリスト全体 を( )でくくる。斜線の位置では改行が行われる: 以下で w,d,n には具体的な数字を 書く。 Iw 整数データを,w文字幅に,右詰で書く。 Fw.d 実数データを,w文字幅に,右詰で,小数点以下d桁で書く。(w≧d+3) Ew.d 実数データを10の指数表式で書く。(w,dの意味は同上) (w≧d+7) Aw 文字データを,w文字幅に,左詰で書く。wを省略すると,対応する文字 データの長さに等しい幅に書かれる。 以上のものは,繰り返し数rを前に付けることができる。(例 3F10.5 の3) r=1は 省略可能。また,指定した幅がデータの長さより短いときには,エラーとなり*****印 が書かれる。 nX n個の空白を書く。標準では,n=1も必ず 1X と書き1を省略できない。 '文字列' 文字列を出力する。 "文字列" 同上 [90] / 改行する。 $(または \) 改行しない。 書式リスト全体をくくる( )以外に,要素のいくつかを部分的にまとめたものを( )で くくり,その前に繰返し数を書くこともできる。 例 "(A8,5(I4,'=',F10.5))" 注)書式付きのREAD文も同じように書くことができるが,指定された書式どおりに 入力しないと間違いを起こすことになる。したがってここでは省略し,詳しく は(→7章7-2)「ファイル入出力」で扱う。ただし, 文字データの入力文に「A指定」を利用すれば,キーボード入力の際に' 'また は" "で囲む必要はないので便利である。「*指定」でも' 'または" "なしで も読みとってくれるが,文章の場合,最初のコンマ「,」または空白で「データ の区切り」とみなされ,後ろは読まれない。 ============================================================================== (クイズ)次のプログラムを実行したら,何が出力されるだろうか?[fixed_pt.f90] CHARACTER::q*1,f*69 q="""" f="('CHARACTER::q*1,f*69'/'q=',4A/'f=',3A/'PRINT f,q,q,q,q,q,f,q'/'END')" PRINT f,q,q,q,q,q,f,q END
============================================================================== 5.3. 途中で抜ける 5.3.1. EXIT文とCYCLE文 EXIT文 : ループを抜けて,END DO文の次の文へ実行を移す。 [90] CYCLE文: ここからEND DO文までの間をスキップし,END DO文(したがって ループの先頭)へ実行を移す。 [90] いずれも [77] 方式では,行き先の文に文番号を付けることにより,GOTO文で書く ことができるが, [90] では文番号を全く使わずにプログラムを書くことができるよ うに改良された。 例題 5_4「クラスの生徒の1科目の試験点数(整数値)を次々に読み込み, 負の数が入力されたら入力を終了し,平均点と標準偏差(ともに実数 値)を求める。」[ex5_4.f90] ! --- Mean and Standard Deviation --- INTEGER :: n=0, i, mark REAL :: w = 0.0, v = 0.0, mean, sd DO PRINT '(A, $)', "点数(整数:終了は負の数)は? " READ*, mark IF(mark < 0) EXIT ! これでループを抜ける。 n = n + 1; w = w + mark; v = v + mark**2 END DO IF(n>0) THEN mean = w/n ; sd = SQRT(v/n - mean**2) PRINT '( "平均点 =", F5.1)', mean ! 平均・標準偏差は小数点 PRINT '( "標準偏差=", F5.1)', sd ! 以下1桁で十分である。 ENDIF PRINT '( "生徒総数=", I5)', n END
例題 5_5 「2つの正の整数の最大公約数を求めよ。」[ex5_5.f90] ! --- 最大公約数を求める --- INTEGER :: m, n, k PRINT*, 'Input two integers m, n ( > 0):' READ*, m, n IF(m > 0 .AND. n > 0) THEN DO k = MOD(m, n) IF(k==0) EXIT m = n ! 三角トレード n = k END DO IF(n>1)THEN PRINT*, '最大公約数=', n ELSE PRINT *,'公約数なし' END IF END IF END
例題 5_6 「100までの素数を求める。」[ex5_6.f90] ! --- Prime Number upto 100 --- INTEGER :: n, m, max=100 PRINT *, 2 ! 2以外の偶数は調べる必要がない。 xxx: DO n = 3, max, 2 DO m = 3, NINT(SQRT(REAL(n))), 2 ! √nまでの奇数を調べればよい。 IF( MOD(n,m)==0 ) CYCLE xxx ! ラベルxxx のループへ出る。 END DO PRINT *, n ! ループを通過すれば素数である。 END DO xxx ! CYCLE xxx で,ここへ来る。 END
ループが多重になっている場合に,現在のループを飛び越して外側のループをも抜け 出したい(あるいは外側のEND DO文へ移りたい)ときには,DO文にラベルを付け,この ラベルを指定する。 5.3.2. DO WHILE文 DO WHILE(論理式) ..... END DO 論理式の値が真の間だけループを繰り返す。ただし,ループの途中で論理式が偽に なったときでも,ループは実行され,DO文に戻ってから判断・選択が行われることに 注意。DO i = 1,100 i = 1 i = 1 ...... DO WHILE( i <= 100 ) DO WHILE( i <= 100 ) END DO ..... i = i + 1 i = i + 1 ......... ↑ 同じ → END DO END DO これは i=101 も実行される。
例題 5_7「漸化式 x(n)=( x(n-1) + r/x(n-1) )/2 で作られる数列が,n→∞で √r に収束することを利用して,実数rの平方根xを求めよ。ただし, |x**2−r| が0.000005より小さくなれば解が得られたとする。」[ex5_7.f90] ! --- Square Root --- REAL :: r, x, d = 0.000005 PRINT*,'Input a real number:' READ*, r IF(r > 0) THEN x = r ! 初期値は0以外なら何でもよい。 DO WHILE( ABS(x**2 - r) > d ) x = 0.5*( x + r/x) END DO PRINT '(F8.5)', x END IF END
注)制御変数を持たない DO文 や DO WHILE文 は,出口条件がいずれは必ず成立する ことの確信がない限り使用しない方が賢明である。あるいは終値を指定する制御 変数と併用する方がいい。もし条件が成立しなければ無限ループにはまり込んで しまうからである。 5.4. DO型出力リスト 1行に同じデータを繰り返して出力したり,6章の配列要素を1行に出力するのに 用いる。 例1 1から9までの奇数の平方根を,1行に出力する: CHARACTER:: fmt*25 fmt = '( 5(2x, I1, ":", F9.6) )' ! 書式を文字変数で指定 PRINT fmt, ( n, n**0.5, n = 1, 9, 2) (出力) 1: 1.000000 3: 1.732051 5: 2.236068 7: 2.645751 9: 3.000000 例2 #をn個(n<100)横に並べて書く: PRINT '(100A1)', ("#", i=1, n) これに対して DO i = 1, n PRINT '(A1)', "#" END DO では,PRINT文の実行のたびに改行されるため,#が1行に1個,縦にn個並ぶ。 注)書式指定:繰り返し使用する要素をかっこでくくり,その前に繰り返し回数を 書くことができる(例1)。また指定した記述欄のうち,余るものは無視され, エラーにはならない(例2)。 =============================================================================== (丸め誤差と桁落ち)実数(小数点付きの数)は有限の長さの2進数で表現される ため,一般に必ず誤差を伴うと考えてよい。無理数はもちろん,例えば10進数の0.1 でも2進数では無限小数になるから近似的にしか表現されない。これに起因する誤差 を「丸め誤差」という。例えば,桁違いに大きさの違う数の加減算を行うと,大きな 誤差を生じることがある。 試しに 1.0 に 0.00000001 を1億回たす計算をやってみ よ。また,ほぼ同じ大きさの実数の引き算を行うと,結果の有効数字は極端に短くな ってしまう。これを「桁落ち」という。例えば2次方程式 x2 -2bx + c = 0 で c が 非常に小さいとき,解の公式を正直に使って x = b±(b2-c)1/2 で計算すると,一方 の解の精度は極端に悪くなる。この場合,この解は b > 0 ならば x1=b+(b2-c)1/2, x2=c/x1とすることで有効数字は失われずにすむ。 =============================================================================== ⇒5章先頭へ ⇒6章へ