前回,基本的なプログラムによって,とりあえずタートルを動かしてみた. その結果,命令(制御関数)のひとつひとつに反応して, タートルが状態を変化させることがわかったハズである.
様々な命令をたくさん羅列すれば, どんなに複雑な図形でも描くことができる. しかし,同じ命令を何度も羅列するのは高コストである.
そこで,本授業では,より効率的な(複雑な図形をより少ない命令で描く) 方法を探って行く.
本授業では,効率化の具体的な方策として, 制御構造と関数とを利用する.
制御構造には, 連接(羅列,順次), 選択(条件判断), 反復(繰り返し)の3種類がある.
今回は,反復(繰り返し,ループ)による効率化を理解しよう. 例えば,ある動作を 100 回繰り返す場合, その動作に対応する一連の命令を人間が 100 回も書く のは明らかに無駄である. 効率的なプログラミングとしては, その一連の命令を 1 回だけ書き, 繰り返し回数を指定するだけでよい.
C言語の場合, for 文または while 文を利用して, 反復処理を記述する.
作業に移る前に,今回も, 作業用ディレクトリの準備をお忘れなく. また,C言語プログラムのコンパイルと実行の方法について, 前回の説明を再確認しておこう.
例として,多角形の描画を考える. まずは,6角形を羅列的にプログラミングしてみよう. 一定距離の前進と 60 度の回転を6回実行すれば6角形になる.
ソースファイル polygon.c:(効率の悪い例)
#include "kame3d.h" int main() { Init("Kame3D Polygon"); Goto(-1.0, -1.732); Move(2.0); Turn(60.0); Move(2.0); Turn(60.0); Move(2.0); Turn(60.0); Move(2.0); Turn(60.0); Move(2.0); Turn(60.0); Move(2.0); Turn(60.0); Play(); return (0); }
この例では, まったく同じコード 「Move(2.0); Turn(60.0);」を 6回も羅列している.
また,このコードを改造し頂点(角)の個数を増やせば,円に近づけることも可能である. しかし,そのためにソースコードをどれだけ肥大化させなければならないだろうか?
もちろん,現代の PC では, コピー&ペーストの手作業の繰り返しによって, 何行でも簡単に羅列できるので,そうしたくなるのが人情だが, それをやってはいけない. 後で,変更したいとき等,余計な時間・労力がかかることになるし, 不注意による間違いも混入しやすくなってしまう. 人間が働くのではなく, コンピュータを働かせること. 人間は手よりも,頭の方を多く使うべきだ.
では,次の通り, for 文 を利用して, polygon.c を書き換えよう:(効率の良い例)
#include "kame3d.h" int main() { int i; // 反復回数のカウンタ Init("Kame3D Polygon"); Goto(-1.0, -1.732); for (i = 0; i < 6; i++) { // ここから6回反復 Move(2.0); Turn(60.0); } // ここまで Play(); return (0); }
実行結果は最初のものと変わらないのに, ソースコードを非常にコンパクトに記述できた. また,その効果は,反復回数が多いほど,より強力になる. 反復回数を変えて試してみよう.
int n = 12; // この値を変えるだけで任意の多角形を描ける ... for (...; i < n; ...) { ... Turn(360.0/n); }
なお,for ループの標準的な使い方は次の通り:
int カウンタ; for (カウンタ = 0; カウンタ < 反復回数; カウンタ++) { 繰り返したい処理 ... }
このループを実行すると, カウンタの値が 0, 1, 2, ..., 反復回数-1 まで増加しながら, ループ内の処理(命令のグループ)が反復される. カウンタの値が反復回数以上になったとき, 反復は終了する.
なお,for ではなく,while を使っても同様な処理が可能: (だがこれは,オススメできない.)
int カウンタ = 0; while (カウンタ < 反復回数) { 繰り返したい処理 ... カウンタ++; }
for と while の使い分けの基本方針:
while の適切な利用方法については,次回,解説したい.
polygon.c を元にして, 反復処理で何か素敵な図形を描け. (前回作成した図形を複数個ならべてみるとか.)
ヒント: for ループを二重・三重にしたり, ループ内に色々な命令を詰め込んだり, 移動距離や回転角度をカウンタ値から計算したりすると, 面白い図形ができあがる. どんな命令(制御関数)があったか, 前回の説明を再確認しよう.
サンプルソースファイル:
これらのソースファイルについては, 本日の作業用ディレクトリにダウンロードして使うとよい.
ダウンロード方法:
羅列的な悪い例:
Move(0.2); Turn(30.0); Move(0.4); Turn(30.0); Move(0.6); Turn(30.0); ... Move(2.0); Turn(30.0);
これは,「まったく同じ」ではないが, 「同じような」コードの羅列だ.
改善案 (1):
int i; for (i = 1; i <= 10; i++) { Move(i*0.2); // 異なる部分を規則性によりコンパクトに表現 Turn(30.0); }
改善案 (2):
int i; double d[10] = {0.2, 0.4, 0.6, ..., 2.0}; // 異なる部分だけを羅列 for (i = 0; i < 10; i++) { Move(d[i]); Turn(30.0); }
要するに,コード同士の規則性(パターン)を見つけ出し, それを一般化できれば, 羅列的な複数のコードを一つにまとめることができる.
for 文を2個以上利用して, 規則性のある独自の図案を効率良く描くような タートルグラフィックスプログラムを自由に作成せよ.
担当教員へレポートを送信せよ:
SS画像作成方法は前回と同様.