たくさんの値をまとめて扱う

今回は複数の値をまとめて扱うことで、簡単なデータ分析をしたり、多くの物体をアニメーションさせたりする方法を学びましょう。

配列

変数のように1つ1つのデータに名前を付けるのではなく、たくさんのデータをまとめて扱える方法として 配列 (array) という仕組みが用意されています。 配列は次のような文法で作成します。括弧は角括弧 [ ] です。

[データ, データ, データ, ...]

配列にも名前がないと不便なので変数や定数に代入して使います。

let 配列名 = [データ, データ, データ, ...];
let 配列名 = []; // 空の配列を作ることもできる

配列内の個別データには何番目のデータかを表す番号 (index) が振られ、その番号を指定することで配列内の各データを扱うことができます。 また、末尾・先頭にデータを追加することができます。 作成した配列を扱う文法は次の通りです。indexは0から始まることに注意しましょう。

配列名[N] // N番目の値を参照する
配列名[N] =; // N番目に値を上書き代入する
配列名.push(); // 末尾に値を追加する
配列名.pop() // 末尾の値を削除する
配列名.unshift(); // 先頭に値を追加する(元々あったデータの番号が1ずつ後ろにずれる)
配列名.shift(); // 先頭の値を削除する

配列の説明

コラム: Stack と Queue

push と pop は「後に入れたデータを先に出す」という特徴を持ったデータ構造「スタック (Stack)」に対する操作の呼称としてよく使われるものです。 データを押し込む・取り出すイメージです。 身近な Stack っぽい状態の例:

  • 本の山、いわゆる積読(上に積んだ本を先に取る)
  • 雑な収納(全部取り出さないと先に入れたものが出せない)
  • タスク管理が苦手な人の脳内(最近思いついたことから先にやってしまうので〆切が守れない)

逆に「先に入ったデータが先に出る」ような構造は「キュー (Queue)」と呼ばれます。先に並んだ人が先にお店に入れる行列のイメージです。 日常に例えると Stack はダメ人間で、 Queue はきちんとしているイメージが付きまといますが、情報科学的には Stack も大変活躍することがあるものです。 みなさんの部屋や脳内は Stack ですか? Queue ですか?

配列を用いるアニメーション(1): パラパラ漫画風

前回は、跳ね返りや重力など自然界の規則を模倣するなど何らかの規則に従った動きのアニメーションを作りました。 配列を使うと、あらかじめ用意した複数のデータを切り替えていくパラパラ漫画のようなアニメーションを簡単に作ることができます。

用意したデータをルーレットのように順番に表示していくアニメーションの例を見てみましょう。 マウスのボタンを押している間、順番に文字列が切り替わり、離すと止まるものを作ります。

おっとその前に、文字列は初登場でした。

文字列

文字列はダブルクォート " またはシングルクォート ' で囲んで表します。

let name = "山田太郎"; // 補足:シングルクォートでもよい
const grades = ["秀", "優", "良", "可", "不可", "履修取消"];

p5.js で文字列を描画するには text() 関数を使います。 文字のサイズや描画の基準位置を変更するには textSize()textAlign() 関数を使います。

function setup() {
createCanvas(200, 200);
text("text", width / 2, 30);
textSize(32); // 文字サイズを32に設定
textAlign(CENTER, CENTER); // 文字の基準位置を中央に設定
text("テキスト", width / 2, height / 2); // 画面中央に描く
}
実行結果

描画したい文字列が固定のときはこのように直接 text() に渡せばよいですが、変わるときには変数や配列を使います。

作品例:成績ルーレット

それでは、文字列をルーレットのように切り替えながら表示するプログラムを ProgTouch で作ってみましょう。

ステップ 1 / 4
文字列を格納する配列と、現在何番目を表示しているかの変数が必要だ 宣言はどこに?
function setup() {
createCanvas(200, 200);
}
function draw() {
background(220);
}
実行結果

周期的なアニメーションでは割った余りを使うと便利で、ここでは配列の長さ grades.length で割った余りを使うことで、配列の長さの範囲内で i を循環させています。

配列を用いるデータ解析と可視化の基礎

続いて配列の数字を棒グラフとして描いてみましょう。

乱数

今回の例では点数データを用意する必要があります。 たくさんの値を手作業で用意するのは面倒なので、min 以上、max 未満の無作為な値を用意してくれる random(min, max) を使いましょう。 繰り返しと組み合わせることで一気にたくさんの値を用意することができます。

let scores = [];
for(let i = 0; i < 10; i++){
scores.push(random(60, 100)); // 60以上100未満のランダムな数を追加
}

配列と繰り返し

配列のすべての値について順番に繰り返しで処理するのは定番パターンのプログラムです。 配列名.length でデータの個数がわかるのでそれを繰り返しの条件に使います。

function setup(){
createCanvas(200, 200);
// 点数を乱数で用意する(上述)
let scores = [];
for(let i = 0; i < 10; i++){
scores[i] = random(60, 100);
}
// 棒を繰り返しで描く
const dx = width / scores.length; // 棒の幅(固定)
for(let i = 0; i < scores.length; i++){
const h = height * scores[i] / 100; // 棒の高さ(点数に比例)
fill(128);
rect(i * dx, height - h, dx, h);
}
}
実行結果

演習問題ではこの発展編として、最大値・最小値・平均値を求め、棒グラフを塗り分けるプログラムを作成します。 たとえば配列に含まれる値の合計値を求めるプログラムは以下のようになります。

let scores = [88, 80, 76];
let sum = 0;
for(let i = 0; i < scores.length; i++){
sum += scores[i];
}
console.log(sum);

値を1つずつ足し合わせていくことになりますので、途中結果を保持しておくために変数 sum を使用しています。

オブジェクト

次は、複数の物体をアニメーションさせるプログラムを作ります。

配列はたくさんの同じ種類のデータを扱うにはとても便利でした。 それ以外にも複数の異なる種類のデータをまとめて扱いたいときがあります。 たとえば、複数の物体がアニメーションするプログラムを作るときには、各物体の位置・速度・形状・色などをセットで扱うことができると便利です。 そのようなときに利用できるのが オブジェクト です。 オブジェクトではデータに番号ではなく キー と呼ばれる文字列を付けて区別します。

具体的な例を見てみましょう。

const b = { x: 40, y: 40, vx: 0, vy: 3, size: 20 };

と書くと、以下の図のようなデータ構造が作成されます。

オブジェクトの説明

オブジェクト作成の文法は以下の通りです。キーと値をコロン : でつないだものが一組で、それをカンマ , でつなぎます。 長くなる時はカンマで改行して整形すると読みやすいでしょう。

let オブジェクト名 = { キー1:1, キー2:2, ... };
// 長い場合は複数行にまたがって書いてもよい
let オブジェクト名 = {
キー1:1,
キー2:2,
...
};

オブジェクトと配列の組み合わせ

オブジェクトと配列は組み合わせて使うことができます。 たとえば、位置・速度・大きさを持ったボールを二つ作成して配列に追加するプログラムは以下のようになります。

let balls = []; // 配列を用意して
let b1 = { x: 50, y: 50, vx: 3, vy: 0, size: 10 }; // ボールのオブジェクトを作って
balls.push(b1); // 配列に追加
let b2 = { x: 40, y: 40, vx: 0, vy: 3, size: 20 }; // ボールのオブジェクトを作って
balls.push(b2); // またもや配列に追加

ここまで実行するとデータは以下のような状態になります。

オブジェクトと配列を組み合わせた結果の説明

オブジェクトの値を参照するとき・上書きするときは配列と同じように [ ] を使うか、ドット . を使います。

オブジェクト名["キー"] // 値を参照する方法その1
オブジェクト名.キー // 値を参照する方法その2
オブジェクト名["キー"] = 新しい値; // 値を上書きする方法その1
オブジェクト名.キー = 新しい値; // 値を上書きする方法その2

配列を用いるアニメーション(2): たくさんの物体

それではお待ちかね、たくさんの物体のアニメーションです。

let balls = [];
function setup(){
createCanvas(windowWidth, windowHeight);
}
function draw(){
background(160, 192, 255);
// すべてのボールを描画し、速度の分だけ移動させる
for(let i = 0; i < balls.length; i++){
let b = balls[i];
ellipse(b.x, b.y, b.size);
b.x += b.vx;
b.y += b.vy;
}
}
function mouseDragged(){ // ドラッグされたらボールを増やす
const dx = mouseX - pmouseX;
const dy = mouseY - pmouseY;
if(mag(dx, dy) > 5){ // mag(x,y) はベクトル(x,y)の長さ
const b = { x: mouseX, y: mouseY, size: 20, vx: dx, vy: dy };
balls.push(b);
}
}
実行結果

マウスをドラッグすると、その動きの方向に飛んでいくボールが追加されるように作っています。

際限なく物体が増えていくのはよろしくないので画面外に出ていったら配列から削除するなどした方がよいです。 演習問題のひな形プログラムにはその処理も含まれていますので見てみてください。