Programmierkurs
für Naturwissenschaftler/innen

配列

配列は言語によって扱い方が大きく異なるので、他の言語を学ぶ際には注意を要する。

配列とは (まずは概要)

詳しい説明は後にするとして、まずは配列がどんなものかについてざっくりと見てみよう。

実験データーがあってそれの処理 (計算) をしたいとする。 そのデーターが例えば 100 個あったとして、プログラムを書く際に、

let data1 = 0.10;
let data2 = 1.2;
let data3 = 3.2;
  :
  :
let data100 = 1.1;

と書いたとしよう。 もしこの和を求めようと思ったら、

let sum = data1 + data2 + (省略) + data100;

と書かなくてはならない。大変面倒である。 もし、追加のデーターが得られたとしたら、さらに、

let data101 = 0.92;
let data102 = 5.8;
let data103 = 7.2;
  :
  :

と追加した上で、 sum を求める部分も変更しなくてはならない。 これはただの和なのでまだ楽であるが、より複雑な計算 (例えば分散の計算) であればもっと面倒になるであろう。 手で計算した方がよっぽど早いということにもなりかねない。

そこで、配列の登場である。

let data = [ 0.10, 1.2, 3.2, ... ];

のように書く。 このとき各データーは data[0], data[1], … として使うことができる。 上の和を求めるプログラムは、

let sum = data[0] + data[1] + (省略) + data[99];

と書き直すこともできるが、もっと簡単に、

let sum = 0;
for (let i = 0; i < data.length; i++) {
  sum += data[i];
}

と書くことができる (data.length についてはこの後で述べる)。

データーが増えたとしても、 let data = ... の部分だけ変更すれば良く、和を求める部分は一切変更しなくてよい。 プログラムも短くなり、従って見通しも良くなる。

このように、複数のデーター (値) をまとめたものが配列である。

配列の使い方

では配列について詳しく見ていくことにしよう。

定義

配列を定義するには上の例にもあるように、

let data = [ 10, 20, 30 ];

などのように書く。 左辺はいままでの変数の定義と同じである。 右辺が配列特有の書きかたである。 かぎ括弧の中にデーターをコンマで区切って書く。 それぞれのデーター (ここでは 10 とか 20 とか) のことを配列の「要素」という。

通常の変数のように宣言と初期化を分けて書くこともできる。

let data;
data = [ 10, 20, 30 ];

JavaScript では、配列の内容を console.log() で表示することができる。

次のプログラムを入力し、実行してみよ。

let data = [ 10, 20, 30 ];
console.log(data);

実行結果

配列の要素を使う

配列の個々の要素を扱うには、 data[1] のように 変数名 + [ + 数字 + ] とする。 この数字を「添字」(そえじ) という。 JavaScript の 配列の添字は 0 から始まる 。 従って最初の要素を表示するには、 console.log(data[0]) とする。

次のプログラムを入力し、実行してみよ。

let data = [ 10, 20, 30 ];
console.log(data);
console.log(data[0]);
console.log(data[1]);
console.log(data[2]);

実行結果

配列の要素数 (長さ) を知るには

配列の要素数のことをしばしば「長さ」という。 配列の長さは、 配列名.length で取得できる。 例えば、

let data = [ 1, 2, 5 ];
console.log(data.length);

を実行すると 3 が表示される。

少しややこしいが、「 配列.length 」は配列の 要素の個数 である。 一方、配列の 最後の添字 は「 配列.length - 1 」であることに注意されたい。

ある配列の総和を計算したい場合は、

let data = [ 1.2, 3, 5, 1.1 ];
let sum = 0;

for (let i = 0; i < data.length; i++) {
  sum += data[i];
}

と書ける。 for 文の条件に <= ではなく < が使われていることに注意して欲しい。

もしかすると、

let data = [ 1.2, 3, 5, 1.1 ];
let sum = 0;

// ! 間違いではないが避けるべき書き方 !
for (let i = 1; i <= data.length; i++) {
  sum += data[i - 1];
}

あるいは、

let data = [ 1.2, 3, 5, 1.1 ];
let sum = 0;

// ! 間違いではないが避けるべき書き方 !
for (let i = 0; i <= data.length - 1; i++) {
  console.log(data[i]);
}

と書きたくなるかもしれないが、これは望ましくない。 for (let i = 0; i < data.length; i++) という書き方に慣れよう。

また、 プログラム中にデーターの個数を直接書いてはいけない:

for (let i = 0; i < 4; i++) { // ! 悪い書きかた !
  sum += data[i];
}

これは文法的には間違っていないが、データーの要素数が直接プログラム内に書かれており (このような書き方をハードコーディングという)、バグの温床になる。 データーが増えたとき、最小限の変更で済むように、この場合はデーター のみ を変更すれば良いようにしておくべきである。

配列の長さが必要な場合は、必ず .length を使って書こう

次のプログラムを入力し、実行してみよ。

let data = [ 10, 20, 30 ];
console.log(data);
console.log(data.length);

for (let i = 0; i < data.length; i++) {
  console.log("data[", i, "] =", data[i]);
}

実行結果

次のプログラムを入力し、実行してみよ。

let data = [ 10, 20, 30 ];
console.log(data);
console.log(data.length);

let sum = 0;
for (let i = 0; i < data.length; i++) {
  sum += data[i];
}
console.log(sum);

実行結果

  1. 次のプログラムを入力し、実行してみよ。

    // ! 良くない書き方 !
    
    let data = [ 10, 20, 30 ];
    console.log(data);
    console.log(data.length);
    
    let sum = 0;
    for (let i = 0; i < 3; i++) {
      sum += data[i];
    }
    console.log(sum);
    
  2. 3 行目の data に追加のデーターを書け。 例えば、

    let data = [ 10, 20, 30, 40 ];
    

    そして、全ての要素の合計が計算されるように (つまり、40 を追加した場合は最終結果として 100 が得られるように) プログラムを変更せよ。

  3. なぜこの書き方が良くないか、検討せよ。

実行結果

初期化についての注意

すでに説明したように、配列を使用する際には、

let data;
data = [ 1, 2, 3 ];

のように書く。

しかし、

let data;
data[0] = 1.2;

とはできない。

どうしてもこのように書きたい場合は、最初に空の配列を書く必要がある。 つまり、

let data = [];	// data は(空の)配列である
data[0] = 1.2;

とする。

(発展) 配列のコピー

少々高度な内容になるが、ひっかかりやすい点なので説明しておく。 最初は流し読み程度でよい。

「配列のコピー」と言う場合には注意が必要である。 ある配列のコピーを作りたくて次のように書くと、おそらく期待した結果にならない。

let array1 = [1, 2, 3];
let array2 = array1;

このとき、 array1 の最初の要素を書きかえたいとする。

array1[0] = 2;

この時何が起こるか見てみよう。

以下のプログラムを実行すると何が起こるか (表示されるか) 考えよ。 その後で、実際に実行してみよ。

let array1 = [1, 2, 3];
let array2 = array1;

console.log("Initial values");
console.log(array1);
console.log(array2);

array1[0] = 2;

console.log("After change");
console.log(array1);
console.log(array2);

実行結果

このとき、 array1 のみが変更されること、つまり

[2, 2, 3]
[1, 2, 3]

という結果が得られることを期待するであろうが、実際には array2 まで変更されてしまっている。 何が起こっているのだろうか。

これはホワイトボードで説明すると、配列用のホワイトボードについている「名前」にもう一つの名前を付けていることになる1

/images/whiteboard-array-empty.png

従って、配列をコピーするには、まず新しい配列を作成し、要素を一つずつコピーする、という手順が必要になる。 実際に配列のコピーを作成する方法にはいくつかあるが、ここでは最も素朴な方法のみ紹介する。

let array1 = [ 1, 2, 3 ];
let array2 = [];

for (let i = 0; i < array1.length; i++) {
  array2[i] = array1[i];
}

以下のプログラムを実行すると何が起こるか (表示されるか) 考えよ。 その後で、実際に実行してみよ。 上の練習問題と比較し、その違いを確認せよ。

let array1 = [ 1, 2, 3 ];
let array2 = [];

for (let i = 0; i < array1.length; i++) {
  array2[i] = array1[i];
}

console.log("Initial values");
console.log(array1);
console.log(array2);

array1[0] = 2;

console.log("After change");
console.log(array1);
console.log(array2);
実行結果

型付き配列

JavaScript では 型付き配列 というものがある。 特に重要なものは、Float64Array (と Float32Array) である 2 。 これ以外の型付き配列に整数値を扱うものもあるが、ここでは説明を割愛する。

既に説明した配列は、自由度が高い (数値以外に文字列等も要素として扱える等) 反面、要素数が多い場合に速度が遅くなることがある。

型付き配列の場合は特定の型 (ここでは実数と考えておいてよい) しか扱えないが、要素数が多い場合に通常の配列より高速に扱うことができる。

具体的には、

let data = new Float64Array(3); // 要素数が 3 の配列を作成

のように宣言する。 今までとかなり違う書き方なので戸惑うかもしれないが、今はこのように書くものだと思って受け入れて欲しい (これを正確に説明しようとするとオブジェクト指向についての知識が必要になる)。

配列の全要素は 0 で初期化される。 つまり、上の文を実行すると [0, 0, 0] という配列が作成される。 一度作成してしまうと、それ以降、配列のサイズ (要素数) は変更できない。 それ以外は通常の配列とだいたい同じである。

宣言と初期化を同時にしたい場合は、

let data = new Float64Array([ 1.0, 2.1, 3.3 ]);

と書ける。 これは配列を引数として与えている。 括弧 [ ] を忘れないように。

以下で考えると分かりやすいかもしれない。

let data0 = [ 1.0, 2.1, 3.3 ];
let data = new Float64Array(data0);  // 上の例では変数を介してではなく、直接配列を引数に書いている

この書きかたで問題はないが、余計な変数 (data0) が必要になるので、できれば上の方法で書くのがよい。

Float64Array のまとめ

// 要素数が 3 の配列を作成。初期値は全て 0
let data = new Float64Array(3);
// 要素数が 3 の配列を作成。初期値は 1, 2, 3
let data = new Float64Array([ 1, 2, 3 ]);

関数と配列

JavaScript では関数の引数や戻り値に配列を使うことができる。

次は配列を引数として受けとる関数の例である (返り値は通常の数値)。

function sumarray(arr) {
  let sum = 0;
  for (let i = 0; i < arr.length; i++) {
    sum += arr[i];
  }
  return sum;
}

let data = [ 10, 20, 30 ];
console.log(sumarray(data));

このプログラムを入力して実行してみよ。

実行結果

練習問題

さて、これまでのまとめとして、次の練習問題をやってみよう。 少なくとも一つ目の問題は実際にやってみて欲しい。

引数として配列を受け取り、その 平均 を求め、それを返す関数を作成せよ。 また、データーとして data = [ 71, 80, 89 ] を与え、この関数を用いこのデーターの平均値を求めよ。

実行結果

引数として配列を受け取り、その 分散 を求め、それを返す関数を作成せよ。 また、データーとして data = [ 71, 80, 89 ] を与え、この関数を用いこのデーターの分散を求めよ。

実行結果

まとめ

ここでは配列について学んだ。

  • 配列とはデーターのまとまりである

  • 配列は let data = [ 1, 2, 3 ]; などで定義できる

  • 各要素は添字によって指定できる

  • 配列の添字は 0 から始まる

  • 配列の要素数は 配列.length として取得できる

  • Float64Array という型付き配列もある。要素数が多い場合に有利である


1

この説明はあまり正しくない。正確に知るにはもうすこし高度な知識が必要となるため、ここではこの程度の説明としておく。

2

Float は浮動小数点数の意であり、64 (及び 32) は 64 (及び 32) ビットで表現できる範囲、という意味である。 要するに 64 の方がより広い範囲の数値を表現できる。