コンピュータプログラミングII (2)

複数の同種の値をまとめて扱う

このスライドの使い方

テキスト等がはみ出した場合は、フォントサイズを小さくして調整する。 「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)

本日のゴール

Processing 開発環境(PDE)のショートカットキー

特に使用頻度の高いものを挙げる。

ファイルの保存 (Save)
Ctrl-S
プログラムの実行 (Run)
Ctrl-R
書式の自動調整(字下げなど) (Auto Format)
Ctrl-T

その他はリファレンスやメニューの記述を参照のこと。 なお、リファレンスに記載はないが、Ctrl+矢印キー なども存在する。

復習: 変数の型

複数の値を組にしたい場面

1組で1つのモノを表す場合

複数の値からなるデータの場合

変数の宣言と初期化 - スカラーの場合

int a;             // 変数の宣言
                   //    - intの値を入れる箱が1つできる
a = 3;             // 代入
                   //    - 箱にを入れる
int a = 3;         // 変数の宣言と初期化

変数の宣言と初期化 - 配列の場合

new 型名[要素の個数]

int[] a;           // 変数の宣言
                   //    - intの値が入る箱の組(配列)を指し示す矢印(参照)を入れる箱が1つできる
a = new int[3];    // 配列の生成および代入
                   //    - intの値が入る箱を3つ連結した組(配列)を作り、
                   //      それを指し示す矢印(参照)を変数 a の箱に入れる

配列の中の1つの箱を要素と呼ぶ。この場合、要素数 3 の配列という言い方をする。

配列の各要素に値を代入

a[0] = 3;         // 0番目の箱(配列の0番目の要素)に値を入れる
a[1] = 9;
a[2] = 7;

まとめ - スカラーと配列

配列の例 - 点(x, y)

配列以前

int x, y;
x = 10;
y = 20;

x, y は組であるが、x, y の関係はプログラマの頭の中にしかない。

配列以後

int[] point;
point = new int[2];    // 0番目がx座標、1番目がy座標
point[0] = 10;
point[1] = 20;

配列の初期化

変数宣言時には次のように初期化することもできる。

int[] point = {10, 20};

要素の区切りにはカンマを用いる。

変数宣言時でなくても、配列の生成時には初期化ができる。

point = new int[] {10, 20};

要素数は勝手に数えてくれるので記述の必要がない。

配列の要素の参照

配列名[添字]

添字(index)で、どの箱を見たいかを示す。添字は 0 から (要素数 - 1) までの値をとる。

int[] point;
point = new int[2];
point[0] = 10;
point[1] = 20;

ellipse(point[0], point[1], d, d);

存在しない箱 (例えば point[2]) を指定するとエラーになる。

配列の要素数(長さ)

配列名.length

この場合 point.length の値は 2。

復習: だんだん大きくなる円

12個の円が横一列に並んでいる。円の直径は左から徐々に大きくなる。

復習: だんだん大きくなる円

size(400, 400);             // ウィンドウのサイズ
background(255, 255, 255);  // 背景色(白)
noStroke();                 // 輪郭線を無効に

int x = 30;                 // 円の中心の x座標の初期値
int y = 200;                // 円の中心の y座標
int dx = 30;                // 円の中心間の距離
int d = 3;                  // 円の直径の初期値
int dd = 2;                 // 円の直径の増分
int n = 12;                 // 円の個数

fill(0, 0, 0);              // 塗る色 (黒)
for(int i = 0; i < n; i++) {
  ellipse(x, y, d, d);
  x = x + dx;
  d = d + dd;
}

練習: 大きさが指定されている円

12個の円が横一列に並んでいる。円の直径には指定があり、規則性はない。

練習: 大きさが指定されている円

もし配列を使わないとすると、1つ1つ直径の値が異なるので繰り返しにできない。

size(400, 400);             // ウィンドウのサイズ
background(255, 255, 255);  // 背景色(白)
noStroke();                 // 輪郭線を無効に

int x = 30;                 // 円の中心の x座標の初期値
int y = 200;                // 円の中心の y座標
int dx = 30;                // 円の中心間の距離
int d;                      // 円の直径
int n = 12;                 // 円の個数

fill(0, 0, 0);              // 塗る色 (黒)

d = 30;
ellipse(x, y, d, d);
x = x + dx;
d = 15;
ellipse(x, y, d, d);
x = x + dx;
d = 40;
ellipse(x, y, d, d);
x = x + dx;
d = 10;
ellipse(x, y, d, d);
x = x + dx;
d = 25;
ellipse(x, y, d, d);
x = x + dx;
d = 20;
ellipse(x, y, d, d);
x = x + dx;
d = 10;
ellipse(x, y, d, d);
x = x + dx;
d = 40;
ellipse(x, y, d, d);
x = x + dx;
d = 5;
ellipse(x, y, d, d);
x = x + dx;
d = 45;
ellipse(x, y, d, d);
x = x + dx;
d = 10;
ellipse(x, y, d, d);
x = x + dx;
d = 35;
ellipse(x, y, d, d);

練習: 大きさが指定されている円

直径の値を格納する配列を導入。

size(400, 400);             // ウィンドウのサイズ
background(255, 255, 255);  // 背景色(白)
noStroke();                 // 輪郭線を無効に

int x = 30;                 // 円の中心の x座標の初期値
int y = 200;                // 円の中心の y座標
int dx = 30;                // 円の中心間の距離
int[] d;                    // 円の直径
int n = 12;                 // 円の個数

fill(0, 0, 0);              // 塗る色 (黒)

d = new int[] { 30, 15, 40, 10, 25, 20, 10, 40, 5, 45, 10, 35 };  // 直径の指定

for(int i = 0; i < n; i++) {
  ellipse(x, y, d[i], d[i]);    // i番目の円の直径 d[i] を取り出す
  x = x + dx;
}

練習: 大きさと色が指定されている円

12個の円が横一列に並んでいる。円の直径には指定があり、規則性はない。円の色の色相は描画前にランダムな値が設定されるものとする。

練習: 大きさと色が指定されている円

練習: 大きさと色が指定されている円

前準備

size(400, 400);             // ウィンドウのサイズ
background(255, 255, 255);  // 背景色(白)
noStroke();                 // 輪郭線を無効に

int x = 30;                 // 円の中心の x座標の初期値
int y = 200;                // 円の中心の y座標
int dx = 30;                // 円の中心間の距離
int[] d;                    // 円の直径
int n = 12;                 // 円の個数
float[] h;                  // 円の色相

d = new int[] { 30, 15, 40, 10, 25, 20, 10, 40, 5, 45, 10, 35 };  // 直径の指定

練習: 大きさと色が指定されている円

つづき

// 色相の値をあらかじめ乱数を使って生成し配列に格納
h = new float[n];
for(int i = 0; i < h.length; i++) {
  h[i] = random(0, 360);    // 0-360の範囲の乱数を発生
}

colorMode(HSB, 360, 100, 100);  // 色の指定をHSBに

for(int i = 0; i < n; i++) {
  fill(h[i], 100, 100);         // i番目の円を塗る色を取り出して指定
  ellipse(x, y, d[i], d[i]);    // i番目の円の直径 d[i] を取り出す
  x = x + dx;
}

練習: 人の並び

身長の異なる 7人の人が並んでいる様子を絵にしたい。 7人のひとりひとりの身長を配列で与えると、描画するプログラムを作成しなさい。 なお、小さい順に並んでいる必要はない。

なお、人はマッチ棒の形をしている必要はない。

練習: 人の並び - 参考

身長の差が等間隔であることにすれば、配列を用いずに書くことができる。

size(400, 400);             // ウィンドウのサイズ
background(255, 255, 255);  // 背景色(白)
noStroke();                 // 輪郭線を無効に

int x = 50;                 // 人の中心の x座標の初期値
int y = 300;                // 人の足元の y座標
int dx = 50;                // 人の中心線間の距離
int h = 145;                // 身長の初期値 [cm]
int dh = 5;                 // 身長の増分
int w = 20;                 // 体の幅
int n = 7;                  // 人数
int d = 30;                 // 顔の直径

fill(0, 0, 0);              // 塗る色 (黒)
for(int i = 0; i < n; i++) {
  ellipse(x, y - h, d, d);
  rect(x - w / 2, y - h, w, h);
  x = x + dx;
  h = h + dh;
}

復習: 配列の要素を先頭から末尾まで順に見る

h = new int[] { 145, 186, 176, 168, 186, 156, 172 };  // 要素数は7

for(int i = 0; i < h.length; i++) {

    h[i] に関する処理

}

練習: 人の並び - 解答例

size(400, 400);             // ウィンドウのサイズ
background(255, 255, 255);  // 背景色(白)
noStroke();                 // 輪郭線を無効に

int x = 50;                 // 人の中心の x座標の初期値
int y = 300;                // 人の足元の y座標
int dx = 50;                // 人の中心線間の距離
int[] h;                    // 身長
int w = 20;                 // 体の幅
int d = 30;                 // 顔の直径

h = new int[] { 145, 186, 176, 168, 186, 156, 172 };  // 身長; 人数は7名

fill(0, 0, 0);                              // 塗る色 (黒)
for(int i = 0; i < h.length; i++) {
  ellipse(x, y - h[i], d, d);               // 頭
  rect(x - w / 2, y - h[i], w, h[i]);       // 胴体
  x = x + dx;
}

練習: 最も背が高い人を色を変えて表示

ひとつ前の練習問題と同じ条件で、最も背が高い人だけ違う色で表示するようにしなさい。 最も背が高い人は1人とは限らないことに注意すること。

下の画面では身長の列を { 145, 186, 176, 168, 186, 156, 172 } としている。

練習: 最も背が高い人を色を変えて表示 - ヒント

「最も背が高い人」の判別方法

最大値の求めかた

練習: 最も背が高い人を色を変えて表示 - 解答例 [1/2]

size(400, 400);             // ウィンドウのサイズ
background(255, 255, 255);  // 背景色(白)
noStroke();                 // 輪郭線を無効に

int x = 50;                 // 人の中心の x座標の初期値
int y = 300;                // 人の足元の y座標
int dx = 50;                // 人の中心線間の距離
int[] h;                    // 身長
int w = 20;                 // 体の幅
int n = 7;                  // 人数
int d = 30;                 // 顔の直径

練習: 最も背が高い人を色を変えて表示 - 解答例 [2/2]

fill(0, 0, 0);              // 塗る色 (黒)

h = new int[] { 145, 186, 176, 168, 186, 156, 172 };

int max = 0;
for(int i = 0; i < n; i++) {
  if(h[i] > max)
    max = h[i];
}

for(int i = 0; i < n; i++) {
  if(h[i] == max)
    fill(0, 0, 255);
  else
    fill(0, 0, 0);
  ellipse(x, y - h[i], d, d);
  rect(x - w / 2, y - h[i], w, h[i]);
  x = x + dx;
}

練習: ヒストグラム

練習: ヒストグラム

サイコロの目を乱数で求める

(int)random(0, 6)

または

int(random(0, 6))

練習: ヒストグラム

配列を度数分布表として使う

int diceSides = 6;     // サイコロの面の数
int[] freq;            // サイコロの目ごとの回数を格納する配列

freq = new int[diceSides];

float floatDiceValue = random(0, diceSides);  // サイコロの目 [0 以上 diceSides 未満の実数]
int diceValue = (int)floatDiceValue;          // サイコロの目 [0 以上 (diceSides - 1) 以下の整数] 
println("目 " + diceValue + " の回数(振 る 前): " + freq[diceValue]);
freq[diceValue]++;                          // 出たサイコロの目の頻度を 1 増やす
println("目 " + diceValue + " の回数(振った後): " + freq[diceValue]);

配列の各要素は 0 で初期化されているため、最初にインクリメント(++)すると 1 になる。

練習: ヒストグラム

文字列の表示

textSize(16);
text("Hello, Processing world!", x, y);

描画の停止

noLoop();

練習: ヒストグラム

int fps = 24;          // フレーム数/秒  (1フレームごとにサイコロを振る前提)
int dY = 60;           // 行の間隔
int dH = 20;           // 度数1当りの棒グラフの長さ
int w = 40;            // 棒の幅
int diceSides = 6;     // サイコロの面の数
int marginTop = 30;    // 上の余白
int marginLeft = 50;   // 左の余白 (文字を除く)
int marginChar = 15;   // 文字の左余白
int[] freq;            // サイコロの目ごとの回数を格納

void setup () {
  size(640, 400);
  frameRate(fps);
  noStroke();
  background(255, 255, 255);
  colorMode(HSB, 360, 100, 100);
  freq = new int[diceSides];
}

練習: ヒストグラム

void draw() {
  int diceValue = (int)random(0, diceSides);  // サイコロの目 [0 以上 (diceSides - 1) 以下の整数] 
  freq[diceValue]++;                          // 出たサイコロの目の頻度を1増やす
  for(int i = 0; i < freq.length; i++) {      // 行(サイコロの目)ごとの繰り返し
    int y = marginTop + i * dY;       // 棒の中心のy座標
    // 行ラベル
    fill(0, 0, 0);                    // 文字の色は黒
    textSize(16);
    text(i + 1, marginChar, y + w / 2);       // 棒の左に i + 1 を表示
    // 棒
    int h = freq[i] * dH;             // 棒の高さ(x方向の長さ)
    if(marginLeft + h + dH > width) {    // 次が書けないほど右に来ているか? (width はウィンドウの幅)
      fill(0, 0, 0);                  // 色を黒に変更
      noLoop();                       // draw() を呼び出す繰り返しは今回で終わり=アニメーション終了
    }
    else {
      fill(360 * i / diceSides, 100, 100);    // 棒ごとに色相を変更
    }
    rect(marginLeft, y, h, w);        // 棒の左上の座標、横、縦
  }
}