第八回 配列の基礎

今回は、複数のデータを格納するためのデータ構造である配列について学ぶ。

配列の宣言と利用

配列とは複数のデータを格納するためのデータ構造である。
以下のプログラムは、整数を5つ格納できる配列 a を定義し、利用するプログラムである。
int a[5];   // 整数を格納できる要素数 5 の配列の宣言

a[0] = 10;    // 配列へのデータの書き込み
a[1] = 2;
a[2] = 0;
a[3] = -5;
a[4] = -23;

// 配列のデータをコンソールに表示
for(int i=0 ; i<5 ; i++)
{
	cout << "配列aの" << i << "番目の値は" << a[i] << "\n";
}
paiza.IO に貼り付けたところが下図である。インデントの揃え方も参考にして欲しい。



実行したときの出力がこちら。



さて、プログラム中で「int a[5];」の部分に着目して欲しい。
これは、整数を5つ格納できる配列 a を定義する命令である。

変数について学んだ時と同じ考え方をすれば、これは「整数を5つ格納できる棚」を用意したことに相当する。



図に示されているように、「int a[5];」という宣言で、a[0]、a[1]、a[2]、a[3]、a[4] という名前の箱 (変数) が使えるようになる、と考えればよい。

このように、「宣言時は 5 とし、使うときは 0 ~ 4 という添字を使う」のが配列を使う際につまづきやすいところである。
これは繰り返し使って慣れるしかない。

[注意]
1 年生で学んだ Visual Basic における配列では、
Dim a(5) As Integer
により、a(0)、a(1)、a(2)、a(3)、a(4)、a(5) の 6 つの変数が使えたことに注意。
ただし、プログラミング言語の世界では、むしろVisual Basic のように 6 つの変数が使えることの方が特殊である。

C/C++、Java、C# などのように「宣言『int a[5];』により a[0] ~ a[4] の 5 つの変数が使える」という形式に早く慣れよう。

上のプログラム例では、配列の 5 つの要素 a[0] ~ a[4] に値を格納する際、以下のように変数と同様に代入文を書いていた。
a[0] = 10;    // 配列へのデータの書き込み
a[1] = 2;
a[2] = 0;
a[3] = -5;
a[4] = -23;
この5つの命令により、以下のような状況が生まれる。
変数に慣れている学生にはそれほど違和感はないであろう。



また、上のプログラム例では、配列のそれぞれの要素に格納された値をコンソールに表示する際、
以下のように for 文を使っていたことに注意して欲しい。
// 配列のデータをコンソールに表示
for(int i=0 ; i<5 ; i++)
{
	cout << "配列aの" << i << "番目の値は" << a[i] << "\n";
}
「for(int i=0 ; i<5 ; i++)」により、「i が 0 から 4 まで変化する」という for 文となり、
a[i] をコンソールに表示することで a[0] ~ a[4] の全てが表示されるのである。

なお、「int a[5];」による宣言では a[5] は使ってはいけない、ということは以下で確認できる。
すなわち、「a[5] = -1;」という a[5] への代入文 (太字で表示した) を一文挿入して実行を試してみるのである。
int a[5];   // 要素数 5 の配列の宣言

a[0] = 10;    // 配列へのデータの書き込み
a[1] = 2;
a[2] = 0;
a[3] = -5;
a[4] = -23;
a[5] = -1;

// 配列のデータをコンソールに表示
for(int i=0 ; i<5 ; i++)
{
	cout << "配列aの" << i << "番目の値は" << a[i] << "\n";
}
これを paiza.IO で実行すると、下図のように「エラー」ではなく「警告 (warning)」となり、実行はできてしまう。
しかし、環境によってはエラーとなる場合もある。
いずれにせよ「int a[5];」で宣言した場合はa[4] までしか使ってはいけないことをしっかりと頭に入れよう。




配列の初期化

変数には、宣言と同時に値を与えておく「初期化」があった。
配列にも同様に初期化がある。以下のように、コンマ (,) で区切ったデータを {} で括って配列に渡せば良い。
int a[5] = {10, 2, 0, -5, -23};   // 要素数 5 の配列の初期化

// 配列のデータをコンソールに表示
for(int i=0 ; i<5 ; i++)
{
	cout << "配列aの" << i << "番目の値は" << a[i] << "\n";
}
これにより、下図のような状況となる。次習の演習ではこの配列の初期化を用いる予定である。



なお、配列の初期化にはいくつかのバリエーションがある。最初から覚える必要はないが、いくつか書き出しておこう。

まず、初期化の場合、左辺の要素数を省略して書くことができる (つまり、a[5] ではなく a[])。
int a[] = {10, 2, 0, -5, -23};   // 要素数 5 の配列の初期化
また、左辺の要素数に対して、右辺の数値の個数が少なかった場合、その要素は 0 で初期化される。
int a[5] = {10, 2, 0, -5};   // 値の与えられなかった a[4] は 0 で初期化される。
また、左辺の要素数よりも多いデータを右辺に与えると、エラーになる。
int a[5] = {10, 2, 0, -5, -23, -7};   // エラー



配列とfor文

さて、先程の例で、配列に格納されているデータをコンソールに表示するために for 文が使われていたことを思い出そう。
配列は、a[i] という形式で書くことで値を格納したり取り出したりできるのであるから、i をカウンタとして使った for 文と相性がよい。

さらに言えば、配列を使いこなすには for 文の理解は必須と言える。
そのような例を見てみよう。

ここで、要素数10の配列に 0以上の3の倍数を小さい方から順に格納する、という例を考える。
一番原始的な方法は下記の通り。
int a[10];  // 整数を格納できる要素数 10 の配列の宣言

// 3の倍数を10個格納
a[0] = 0;
a[1] = 3;
a[2] = 6;
a[3] = 9;
a[4] = 12;
a[5] = 15;
a[6] = 18;
a[7] = 21;
a[8] = 24;
a[9] = 27;

// 要素数 10 の配列のデータをコンソールに表示
for(int i=0 ; i<10 ; i++)
{
	cout << "配列aの" << i << "番目の値は" << a[i] << "\n";
}
見ての通り、3の倍数を列挙して順に格納しているだけである。

しかし、3の倍数のようにルールが明確な数を、上記の様にに列挙して代入するというプログラマはまずいない (いてもクビになるだろう)。
そういう意味では、以下のように配列の初期化を用いるのも駄目な例である。
// 3の倍数を10個格納
int a[10] = {0, 3, 6, 9, 12, 15, 18, 21, 24, 27};
なぜこれらがダメかというと、配列の要素数 (ここでは 10) が 100 や 1000 などと大きくなったときに
すぐに対応できなくなるためである。

このように、配列に格納するデータのルールが決まっている場合は for 文を用いてデータを格納すれば良い。
int a[10];   // 整数を格納できる要素数 10 の配列の宣言

// 3の倍数を10個格納
for(int i=0 ; i<10 ; i++)
{
	a[i] = 3*i;
}

// 要素数 10 の配列のデータをコンソールに表示
for(int i=0 ; i<10 ; i++)
{
	cout << "配列aの" << i << "番目の値は" << a[i] << "\n";
}
for 文は i=0 で始まり i<10 のあいだ繰り返される。すなわち i が 0~9 の間、 a[i] = 3*i; という命令が繰り返される。
それにより、a[0]~a[9] に3の倍数が格納されることがわかるだろう。

それに対し、「i=1 で始まり i<=10 のあいだ繰り返される for文」、
すなわち i が 1~10 の間繰り返される for 文ではここでは問題があることも理解して欲しい。
もちろん、使ってはならない a[10] を使うことになるからである。

このように for 文を正しく用いることで、配列の要素数が 100 や 1000 と増えても問題なく対応できるプログラムとなる。


[演習] 配列で数学の数列を実現してみよう

ルールの明確なデータを配列に格納する際、for 文を用いると良いということを学んだ。
配列と for 文の組み合わせに慣れるため、そのような練習をもう一つ行ってみよう。
この内容の理解が課題を解くために必須であるので、しっかりと身に付けよう。

[演習]
整数を格納できる要素数10の配列 a を宣言する。
配列 a の i 番目の要素 a[i] に、以下の漸化式を満たす数列 ai の値を格納せよ。
ai = 2 ai-1 - 1    (i>=1)
ただし、a0 = 0 とする。

この問題を解くためには、高校数学で学ぶ漸化式について皆さんが正しく理解している必要がある。
といっても、問題文に必要なことは全て書いてあるので、
皆さんが注意深く式や文章を読むことができるかどうかがポイントである。

さて、この節の冒頭で「ルールの明確なデータを配列に格納する」と述べた。
漸化式「ai = 2 ai-1 - 1    (i>=1) 」がこの「ルール」に相当するのはすぐに想像がつくであろう。

それでは、漸化式についている条件「i>=1 (i は 1 以上)」はどういう意味だろうか?
もちろん、漸化式「ai = 2 ai-1 - 1」を i=0 の場合に適用してはいけない、という意味である。

では、i=0 の場合、どのように数列の値 a0 や配列の値 a[0] を扱えば良いのだろうか?
もちろん、問題文にある数列の初期値 a0 = 0 を用いるのである。
すなわち、i=0 の場合は数列でも配列でも特別扱いをしなければならない、ということである。

以上を踏まえると、必要なプログラムは以下のようになる。
int a[10];

// 数列の初期値
a[0] = 0;

// 漸化式は i>=1 のときのみ
for(int i=1 ; i<10 ; i++)
{
	a[i] = 2*a[i-1]-1;
}

// コンソールへの表示は i=0 からで OK
for(int i=0 ; i<10 ; i++)
{
	cout << "配列aの" << i << "番目の値は" << a[i] << "\n";
}
プログラムを良く見ると、漸化式「a[i] = 2*a[i-1]-1;」を計算しているのは「for(int i=1 ; i<10 ; i++)」においてである。
すなわち i が 1 から 9 まで、ということである。

i=0 の場合は、「a[0] = 0;」として特別扱いしていることがわかる。

さて、上記のプログラムを paiza.IO に適切に貼り付けると以下のようになる。
インデントも参考にすること。



実行結果は以下のようになる。





課題→

オンラインコンパイラで C/C++ を自習しように戻る