Last-modified:2018/03/29 22:55:11.
構造化プログラミングの最後の要素である「反復」を覚えよう。 これでかなりのことができるようになる。 ここまでを完全にマスターすることができれば、 コンピュータで解決できる問題は(理論上)ほとんど全て解決できるようになる筈だ。
最も基本となるループは while である。 これ 1つを覚えておけば(多少煩雑でも)ほとんどの問題に対応できるだろう。
ある条件を満たす限り、ループを繰り返す仕組み。
# while.rb
i = 0
while (i<10)
print i, "\n"
i += 1
end
条件文(上のコードで 「i<10
」 の部分)は条件式であり、論理値を返すモノが入る。 上のコードでは i
と 10
との大小を比較し、比較演算の結果真か偽が返るわけだ。 最初に while
に突入したときには、while
の頭で条件判定が行われ、 1回目のループを実行する。 print
で i
の内容を書き出して、i
に1を加えた数を新たに i
とする。 1 何度かループを回ると、そのうち i
は \(10\) になる。 このとき i<10
の演算は偽を返すので、ループには入らず、 ループの外の次の処理に移動する。
条件式が常に真ならば、ループを抜け出せず延々と同じループを繰り返すことになる。
# while.rb
i = 0
while (true)
print i, "\n"
i += 1
end
一度このコードを試してみよう。 プログラムが止まらず、何となく不安な気持ちになるだろう。 プログラムを止めるには、キーボードで Ctrl を押しながら C を押せば良い。
本演習の範囲内では無限ループを使うことはあまりないだろうが、 多くのウィンドウアプリケーション、特にほぼ全てのゲームで、 無限ループが使われている。
while ループは強力だが、時にはループ内で条件判定してループを抜け出したり したい場合がある。 そのような場面において有効な方法がある。
break
: そのループの実行を中止し、ループを終了。next
: 現在のループの残りの処理は行わず、同じループの次のループ処理に入る。redo
: 同じループをもう一度始めから実行。これらは主に if 文と供に使われる。 例を以下に挙げる。
#break.rb
i=0
while (i<100)
print i, "\n"
break if (12 == i)
i += 1
end
多重ループを作ることができる。
#nestingLoop.rb
i = 0
while (i<3)
j = 0
while (j<4)
print "#{i}-#{j} "
j+=1
end
print "\n"
i+=1
end
「while
だけ覚えておけばほとんどの問題に対応できる」と先に述べたが、 ループを適用する場面には幾つかのパターンがあり、 それにはまった時にはそれに適した書式で書いた方が便利だ。 ループ内で変化する条件に依存するのではなく、 ループに入る前に既にループの回数が決まっていることがある。 その場合には以下の書式が便利だ。
# repeatTimes.rb
10.times do |i|
print i, "\n"
end
勿論 while を使って愚直に書くこともできるが、 ループの先頭で条件を、末尾でインクリメント演算を分けて記述するのは面倒だろう。 この形式は非常によく使うので、覚えてしまった方が良い。 2
10.times do |i|
を大雑把に説明すると、10 個の数として 0〜9 を取り出し、 ループを回るごとに 0〜9を1つずつ i
に代入していくという感じだ。 3 特に、0から始まって、(指定した数-1)で終わる点に注意しよう。
数字を直接書かずに、 整数クラスが格納された変数があれば、それから作用させることもできる。
# repeatTimes.rb
n = 12
n.times do |i|
print i, "\n"
end
(n/3).times do |i|
print i, "\n"
end
本章のサンプルコードでループの中身が字下げされていることに気付いただろうか。 ループも頻出する構造で、離れた箇所の対応に注意する必要がある。 今後作成するコードは、必ず字下げを行ってコーディングすること。
1から 100 までの整数の総和を求めるプログラムを作成し、ソースコードと答えを示せ。
円周率\(\pi\)を求める。 \(-\pi \le x < \pi\) で定義された関数 \(f(x) = |x|\) を周期\(2\pi\)で拡張した関数に関する フーリエ級数展開から、 \[\begin{aligned} \frac{\pi^2}{6} = \frac1{1^2} + \frac1{2^2} + \frac1{3^2} + \cdots + \frac1{n^2} + \cdots \end{aligned}\] の関係が得られる。 この式から以下の式が得られる。 \[\begin{aligned} \pi = \sqrt{6(\frac1{1^2} + \frac1{2^2} + \frac1{3^2} + \cdots + \frac1{n^2} + \cdots)} \end{aligned} \qquad(1)\]
式(eq. 1)を用いて円周率 \(\pi\)を求めるプログラムを作成せよ。 ただし、用いる項の数 \(n\) については、まず\(n=1\)について実行し、 次に\(n=10\)で実行, 次に\(n=100\) ……のように一桁ずつ増やしてプログラムを走らせ、 真の円周率 3.141592653589793…. に対する精度(合っている桁数)が どのように変化するかを評価せよ。 \(n\) は \(10^9\) まで行うこと。 4 各段階での実行速度も計測し、先の精度と併せて議論せよ。 最初の幾つかはおそらく「瞬間的に終わる」(このように記述すれば良い)筈である。
例えば、
項の数\(n=1\)の時は \(\sqrt{6(\frac11)} = 2.449\cdots\)。
項の数\(n=2\)の時は \(\sqrt{6(\frac11 + \frac14)} = \sqrt{6(\frac54)} = \sqrt{\frac{15}2} = 2.738\cdots\)。
項の数\(n=3\)の時は \(\sqrt{6(\frac11 + \frac14 + \frac19)} = \sqrt{6(\frac{49}{36})} = \sqrt{\frac{49}6} = 2.857\cdots\)。
平方根を求めるには、Math.sqrt() 関数を使えば良い。
#sqrt.rb
print Math.sqrt(2.0)