コンピュータプログラミングI(5)
複雑な繰り返し・多数の場合分け

July 6, 2017

Nakajima, Yajima
Iwai, Tatsuta
Kakizaki, Ikeda

このスライドの使い方

テキスト等がはみ出した場合は、フォントサイズを小さくして調整する。 「S」 キーで小さく、「B」 キーで大きくなる。また、 下記のキーボード操作が使用できる。(一部キー操作は IE のみ対応)

「←」 or 「Page Up」 前のスライドに戻る
「→」 or 「Page Down」 or「スペース」 次のスライドに進む
「Home」 and 「End」 先頭(Home)または最後(End)のスライドへ移動
「C」 or 「contents?」 をクリック スライド一覧の表示
「F11」 or 「Ctrl+Shift+F (Win)」, 「Cmd+Shift+F (Mac)」 フルスクリーン表示と通常表示の切り替え
「F」 フッタの表示と非表示の切り替え
「A」 全スライド表示に切替 (印刷時に使う)
「S」「-」/「B」「+」 フォントサイズの大(B) 小(S)

本日のゴール

復習: forループの文法(教科書 pp.45-46)

for(init; test; update) {
    繰り返しの中身の処理
}

「test」の部分には、変数と別の値を比べるための式が入る

例
i < 10   <-- iが10より小さい
i > 10   <-- iが10より大きい
i <= 10  <-- iが10以下
i >= 10  <-- iが10以上
i == 10  <-- iが10と等しい
i != 10  <-- iが10と等しくない

この条件が成り立つならばtrue、成り立たないならばfalseとなる
for文の中身は、条件が成り立つ間 (trueの間) は、ずっと繰り返しを続ける
条件が成り立たなくなると (falseになると) 繰り返しは終了し、次の処理に移る

復習: forループを用いたプログラム

size(480, 120);
background(255, 255, 255);
noStroke();
fill(0, 0, 0, 140);

for (int x = 1; x <= 5; x++) {
  ellipse(x*20, 20, 20, 20);
}

複数の変数が絡み合った繰り返し(1)

次のような絵を描くことを考える
円のx座標は、左から順に10, 30, 60, 100, 150, ... であり、 次のような規則性があることがわかる

複数の変数が絡み合った繰り返し(2)

これをforループを使って書いてみよう

int sum = 0;
for (int i = 1; i <= 10; i++) {
  sum = sum + i;
  println(sum);
  ellipse(sum*10, 20, 20, 20);
}

変数sumに注目

sumの値を10倍すれば、目的の計算が行えることがわかる

練習(5-1): 落下運動を静止画で表す

ボールの自由落下を描こう   今回はアニメーションではなく、 次のページの絵のようにボールを横に並べて描くことにする
t秒後のボールの位置ytは、

を用い 次の式に基づいて計算すること

vt = vt-1 + g   (ただしv0は0)
yt = yt-1 + vt

0秒、1秒後、2秒後、... 100秒後のそれぞれのボールを並べる (ボールがウィンドウの外に出ることは、現時点では気にしないでおく)

println("i=" + i + ": " + x + ", " + y);

printlnでボールの位置を表示した例.
printlnは,表示する文字列,変数を+演算子によってまとめることができる.また文字列はダブルクォート(")で括る(詳細は教科書p.231を参照)

前ページの実行例の画面サイズは480x480だが,繰り返し回数が98,99,100のとき,ボールの位置は画面外へ大きく出てしまっていることが分かる. 画面外に図形を描いても,画面には表示されないので無駄な処理を行っていることになる.

繰り返しを途中で打ち切る

練習(5-2): ウィンドウ外にはみだしたら計算を中止せよ

前の練習(5-1)「落下運動を静止画で表す」の例では、たまたま10秒後までのボールをウィンドウの範囲内に描けたが、 何秒後までがウィンドウ内に収まるかは、画面サイズや重力加速度の設定によって異なるはずである

ボールが画面外に出た場合、もはやボールを描くことができないので、計算するだけ無駄である

ウィンドウサイズとボールのy座標を使って判定し、 画面外にボールが出た時点で 計算を打ち切るためにforループから抜け出すように改造してみよう

※ ウィンドウの高さは、Processingの組込み変数 height を使うと得ることができる

画面サイズを480x480とした実行例.10回目はボールが画面外に出たため,繰り返し9回目で処理が終わっている.

「繰り返し」の繰り返し(教科書 pp.48-50)

繰り返しを2重, 3重にも書くことができる

for (int y = 1; y <= 5; y++){
  for (int x = 1; x <= 5; x++){
    繰り返し行う処理
  }
}

この繰り返しでは、まず外側ループで y が 1 に設定され、 内側ループの「x = 1, 2, 3, ..., 5」と変化する繰り返しを行い、 一旦内側ループの処理を終了する
次に、外側ループの 2 回目に入る
y が 2 に設定され、同様に内側ループの「x = 1, 2, 3, ..., 5」と変化する繰り返しを行う
というように y が 5 まで順番に「x = 1, 2, 3, ..., 5」の繰り返しを行う

つまり、x, yそれぞれが
(1, 1), (2, 1), (3, 1), (4, 1), (5, 1),
(1, 2), (2, 2), (3, 2), (4, 2), (5, 2),
(1, 3), (2, 3), (3, 3), (4, 3), (5, 3),
......
(1, 5), (2, 5), (3, 5), (4, 5), (5, 5),
と変化していき、5 * 5 = 25回繰り返しが行われる

例題: 平面上に円を並べる(1)

size(480, 120);
noStroke();
fill(120, 120, 120);
for (int y = 0; y <= height; y += 40) {
  for (int x = 0; x <= width; x += 40) {
    ellipse(x, y, 40, 40);
  }
}

例題: 平面上に円を並べる(2)

円そのものを繰り返しを使って、重ねて表示する

size(480, 120);
noStroke();
fill(120, 120, 120, 20);
for (int y = 0; y <= height; y += 40) {
  for (int x = 0; x <= width; x += 40) {
    for (int r = 2; r <= 40; r += 2) {
      ellipse(x, y, r, r);
    }
  }
}

二重の繰り返しからの脱出

breakによって二重の繰り返しから一気に脱出したい場合、 ラベル付き break を使う

outside: for (int y = 0; y <= height; y += 40) {
  for (int x = 0; x <= width; x += 40) {

    .........
    break outside;   -----二重のforループ全体を打ち切って脱出する----+
  }                                                                  |
}                                                                    |
                                                                     |
                     <-----------------------------------------------+

「outside:」はラベルと呼び、プログラムのある地点を指すやり方 (最後の文字はコロン「:」)
break で飛び先のラベルが指定されると、 ラベルのついたforループまで一気に抜けることができる

単なる break では、1階層分のforしか抜けることができない

練習(5-3): 繰り返しと変数の使いこなし

繰り返しと変数を使いこなして、次のような模様を描いてみよう

もう一つの繰り返しの書き方 - whileループ

while文の文法

while ( test ){
  繰り返し行う処理
}

forよりシンプルな文法: testがtrueであれば(成り立つのであれば)繰り返し続ける

forのような繰り返しの枠組みがないため、自由度が高い書き方ができるが、その分変数の意味や値の変化のしかたを理解する必要がある

whileループの例

繰り返しに関するあれこれ

練習(5-4): ウィンドウ外にはみだしたら計算を中止せよ(2)

前の練習(5-2)「ウィンドウ外にはみだしたら計算を中止せよ」を振り返ると、 繰り返しを続けるか終えるかは「ボールが画面外に出たかどうか」を判断すれば良いのであって、経過秒数は判断には関係がない

例えば、ほぼ無重力状態や、ものすごく広大なディスプレイを想定すると、1000秒後でも画面の外に出ていないかも知れない

そこで、繰り返しの判定に「時刻」を使うのではなく、 「ボールが画面外に出たかどうか」を判定するようにしよう
これを、whileループで書いてみよう

単純には、forは以下のようにwhileに書き換えることができる
ただし、今回は判定に用いる変数も変える必要があるので、もう一工夫しよう

for(int i = 0; i <= 10; i++ ){
  処理
}

  |
  |
  V

int i = 0;
while (i <= 10) {
  処理
  i++;
}

条件分岐のもう一つの書き方 - switch文

switch( 式 ) {
  case 選択肢1:
    処理1
    break;
  case 選択肢2:
    処理2
    break;
  .....
  .....
  default:
    処理
}

式を計算して、マッチするcaseへジャンプする
どのcaseにも該当しない場合は、default以下が実行される (defaultは省略可能)
caseの最後にbreakがない場合、(マッチしていなくても)次のcase内も続けて実行される

switch文の例

switch (x) {
  case 10:
    fill(255, 0, 0);
    break;
  case 20:
    fill(0, 255, 0);
    break;
  case 30:
    fill(0, 0, 255);
    break;
  default:
    fill(255, 255, 255);
}

x座標が、10であれば赤、
20であれば緑、
30であれば青、
その他であれば白に設定する

Processingのその他の機能

乱数と画像を使用した例

//一定時間ごとにハンコ(画像)を描画するプログラム
//10回ハンコを押したら画面を真っ白に戻す
PImage img;
int cnt = 0;
void setup(){
  size(500, 500);
  frameRate(2); //draw関数を1秒間に2回だけ呼ぶように変更する
  img = loadImage("goukaku.png");
  background(255, 255, 255);
}

void draw(){
  if (cnt >= 10)
  {
    background(255, 255, 255);
    cnt = 0;
  }

  float x, y;
  x = random(width);
  y = random(height);

  image(img, x, y);
  cnt++;
}

使用している画像
画像ファイルを保存後,Processingから[スケッチ]-[ファイルを追加...]で使用する画像を選択する.

演習問題

問題1

オセロゲームの碁盤に石が並んだような模様を二重の繰り返しを使って描くことを考える(実行結果は次ページ)

一見、二重の繰り返しで簡単に書くことができそうだが、 良く見ると罫線は縦9本、横9本であり、 碁石は縦8個、横8個であり、個数が一致しない

下のプログラムをベースに、breakをうまく使ってこの図形を描くようなプログラムを作りなさい
このプログラムに、新たにforやwhileを書き加えるなど繰り返しを追加してはいけない
しかし、すでにあるforをwhileに書き換えるのは良い

スケッチ名: FilledOthello
スケッチフォルダ内の.pdeファイルのみを提出

size(600, 600);

for (int y = 50; y <= 450; y = y + 50) {
  line(50, y, 500, y);
  for (int x = 50; x <= 450; x = x + 50) {
    line(x, y, x, y+50);
    fill(255, 255, 255);
    ellipse(x+25, y+25, 40, 40);
  }
}

演習問題

問題2

次の画像は「ナインパッチ」と呼ばれる、昔からある布地の模様付けである
これに準じた模様を多重の繰り返しを用いて描きなさい
形状の規則性が再現できていれば良い、ディテールは各自の表現の自由とする
色は決まったパターンにしても良いし、乱数を使って決定しても良い

なお、1つおきに模様を切り替えるには「割ったときの余りを求める演算子 %」 を使って、2で割ったときの余りを判定に用いれば良い
例: if (x % 2 == 0) .....

スケッチ名: NinePatch
スケッチフォルダ内の.pdeファイルのみを提出