配列とは、ただ一つの名前で複数のデータを管理できるクラスで、データを順番に並べて管理します。 普通のクラスと異なる点は、インデックスと呼ばれる数値(0~)でデータを管理できる所です。

これによって、大量のデータを反復文等で一度に処理できる…それが配列の強みなのです。

配列クラスの使い方

配列クラスは普通の設計図ですから、実物を作る必要があります。 配列クラスの名前はArrayです。 よって、実物を作るのは次のようにします。

var dim = new Array();
				

たったこれだけで、配列クラスを使う事が出来ます。 一からクラスを作る事の難しさを分かっているからこそ、 とても重宝しますので、大いに活用する事にしましょう。

また、次のように書く事も出来ます。

var dim = [];
				

本入門では、後者の方式を採用します。 後に説明しますが、配列は通常のクラスとは違った使い方をするので、 一般的なクラスと区別するためです。

配列の基本

配列クラスは、通常のクラスとは違う特別な書き方をします。 最初は戸惑うかもしれませんが、使ううちに慣れるので問題ありません。

さて、変数が何かを入れる箱なら、配列は変数を多数入れる事が出来る大きな箱です。 例えば、文房具は筆箱、漫画の本なら本棚、というように特定の種類毎に容器を変えますよね。 TJSの配列も考え方は同じで、特定種類の複数の変数をグループ化してしまおうというのが配列の考え方です。

配列

C/C++

TJSではデータ型を自動的に判断してくれますので、 同一配列の中に整数や文字列、オブジェクト等を混同して入れることが出来ます。

よって、C言語の構造体を使わなくても、配列だけで解決できるのです。

しかし、配列の中に入れる変数に名前は必要ありません。 配列という名前の通り、配列クラスは中に入れる変数という箱を、順番どおりに並べて管理しています。

各箱は数字で管理され、箱の並び順は数字の大小で容易に分かります。 つまり、数字が小さいほど最初にある箱です。

配列2

各箱を参照するのは簡単で、次のように書きます。

配列名[箱の数字]

dimという配列の2番目にある箱を使うなら次のように書きます。

dim[2]

この書き方が分かりづらければ、次のように考えてください。

dim.2

配列クラスが管理しているメンバー変数と考える事が出来ますね。 (ただし、実際は2というメンバー変数が存在しているわけではなく、 箱を順番どおりに並べた結果、3番目にある変数という意味です。)

この時、箱に入っているデータの事を要素と呼び、 箱の順番を指す数字をインデックス、または添え字と呼びます。

配列の最初にある箱の数字は0です。勘違いしやすいので注意してください。

0番目の箱、1番目の箱……99番目の箱...

この時、99番目の箱は、0番目から数えて100個目の箱という事になります。

要素に代入する

実際に配列の中に箱を作ります。 代入は変数と同じ考え方なので、代入演算子「=」を使います。 配列は0番目から順番に管理しますので、最初に入れる箱の番号は0です。

var dim = new Array();
dim[0]=1;
dim[1]=100;
				

これで、0番目の箱に1という数字が、1番目の箱に100という数字がそれぞれ代入出来ました。 この使い方は、普通の変数と全く同じです。

では、0番目の箱~9番目の箱という10個の箱を用意し、 それぞれ最初から順番に1~10の数字を代入してみましょう。

var dim = new Array();
dim[0]=1;
dim[1]=2;
dim[2]=3;
dim[3]=4;
dim[4]=5;
dim[5]=6;
dim[6]=7;
dim[7]=8;
dim[8]=9;
dim[9]=10;

System.inform("2番目の箱="+dim[2]);
System.inform("4番目の箱="+dim[4]);
				

うまく表示されましたか?

反復文を使った代入

配列は、その特徴から反復文を使って全要素を操作できます。 これが配列を使う最大のメリットであり、逆に言えば、配列の要素を一つずつ操作したのでは、 配列を使う意義が薄れます。

以下に、while文を使った例を挙げます。

  1. ループ変数iを用意し、配列の最初の添え字0を代入する
  2. 条件式には、ループ変数が配列の希望要素数になるように設定する
var i=0;
while ( i<10 )
{
    ...箱を作る処理...
    ++i;
}
				

この例では、要素を10個用意しました。

箱の数が10個だからといって、変数iが10になるまで処理をさせるのは間違いです。

i<11 ……ダメ!

箱が10個という事は、0番目から数えて9番目までの箱を作るという事だからです。 よって、上のような指定をすると11個の箱が作られてしまいます。

配列[0] 配列[1]...配列[9] 配列[10]

配列番号10は、0番目から数えて11個目の箱。

ただ、while文だとループ変数を用意する手間がありますので、 for文を使って書く方が好まれます。

var dim = new Array();
for (var i=0; i<10; i++)
{
    dim[i] = i+1;
}
System.inform("2番目の箱="+dim[2]);
System.inform("4番目の箱="+dim[4]);
				

変数iは0から始まり、9になるまで処理を繰り返しますから、 この変数iを箱番号の指定に使ってしまおう、という考え方です。 変数iに1を足せば、箱番号+1の数字が代入できるという事になります。

この処理は、次の指定と同じです。

var dim = new Array();
dim[0]=1;
dim[1]=2;
:
中略
:
dim[8]=9;
dim[9]=10;
				

要素の数を知る

配列は管理している箱を普通の変数のように扱えます。配列[箱番号]という指定ですね。

ですが、予め箱の数を決めておき、それを表示するだけなら良いのですが、配列の箱の数が分からないと困る場合があります。 例えば、ユーザから入力と配列を使った簡易データベースを作った場合。 プログラムする段階では、ユーザがいくつのデータを扱うのか知る事は出来ません。

// ユーザが55個のデータを作っていると……

//全ての箱に対して操作
for (var i=0; i<10; i++)
↑これでは9番目の箱までしか操作できない。

//大きな数を指定してしまうと…
for (var i=0; i<100; i++)
↑99番目まで箱が無い!(エラーになる)↑
				

かといって、最初に最大数は100と決めてしまうのもいけません。 学校の生徒数をデータベースにすると、軽く800人を超えるでしょうから、 予め最大データが決まったデータベースほど使えないものは無いですね。

このような状況を避けるために、 配列クラスは管理している箱の数を指すプロパティをメンバーに持っています。 このプロパティ名はcountです。

プロパティcountは、実物(ここでは配列)が持っているメンバーですから、 普通のメンバーの参照と同じように、次のように書けます。

配列名.count

よって、dim.countと書けば、 配列dimが管理している箱の数を求める事が出来ます。 (この箱の数を要素数、配列の大きさ、配列のサイズなどと表現する事があります。)

では、反復文を使って、ある連続したデータを入れた箱0~9を作り、 それを全て表示するという例を見てみましょう。

var dim = new Array();

//10個の箱を作り、代入する
for (var i=0; i<10; i++)
{
    dim[i] = "データ"+i;
}

//変数iがdim.countより低ければ処理する
var str="";
for (var i=0; i<dim.count; i++)
{
    str += i+"番目の箱="+dim[i]+"\n";
}
System.inform( str );
				

これが、配列の使い方の基本となります。

簡単な代入方法

では、配列を使う例の一つを見てみましょう。

あなたが小学校の教師だとして、ある学級のテストの結果(点数)を生徒別、教科別に分けてまとめたいとします。 ただし、教科は主要四教科(国語・算数・理科・社会)とし、生徒数は3名とする。

これまでの知識より、変数を別々に管理するのは宜しくない事は分かると思います。

var name1="山田";
var math1=100; //山田君 算数
var jpn1=91; //山田君 国語

このように共通した変数がある場合、新しくクラスにすれば……と考えますが、 単純にテストの結果をまとめ、全て表示するだけの簡単な処理ですから、わざわざ自分でクラスを作るまでもありません。 よって、ここでは配列クラスを使用します。

var Yamada = new Array(); //山田君
var Saito =new Array(); //斉藤君

しかしながら、配列の箱一つ一つに代入していくのは少し面倒です。 代入したいのはテストの点数ですから、規則的な数では無いので反復文では代入出来ません。

var Yamada = new Array();
Yamada[0] = "山田";
Yamada[1] = 100; //国語結果

人数分の入力をするのは少々骨が折れます。 これを簡単な書き方で代入出来ます。

var Yamada = ["山田",100,90,88,95];
var Saito = ["斉藤",65,75,80,99];
:
				

newを使っていませんが、この場合でもオブジェクト(実物)が作られ、 同時に値を設定出来ています。(コンストラクタに引数を渡しているとも考えられますね。)

これを分かりやすいように図解すると、次のようになります。

配列3

この例では、Yamadaという名前の配列オブジェクトを作っています。 0番目の箱に生徒の名前を入れ、1番目~4番目の箱には、それぞれ「国語」「算数」「理科」「社会」の順番で点数を入れる事にします。

では、3名分のデータを配列で管理し、 全てのテスト結果を表示させるサンプルを見てみましょう。

var datas = ["名前","国語","算数","理科","社会"];
var Yamada = ["山田",100,90,88,95];
var Saito = ["斉藤",65,75,80,99];
var Tanaka = ["田中",90,92,74,100];

function pointDisp( pupil )
{
    var tmp="";
    for ( var i=0; i<pupil.count; i++ )
    {
        tmp += pupil[i]+"\t";
    }
    return tmp+"\n";
}

var result="";
for ( var i=0; i<datas.count; i++ )
{
    result += datas[i]+"\t";
}
result += "\n";
result += pointDisp( Yamada );
result += pointDisp( Saito );
result += pointDisp( Tanaka );
System.inform(result);
				

ところどころ、改行を示す「\n」やタブ文字を指す「\t」を書いていますが、 後は特に難しいような場面は無いと思います。

ただ、この書き方ではとても効率が悪いですね。極端な話、生徒の数が300名分になったとしたら? 一つ一つ生徒用の配列を作り、手作業で関数「pointDisp」を呼ばなければならない。 とにかく煩雑で、修正する箇所が多すぎるのです。

これを解決するため、全ての配列内容を自動で処理できる様に改良します。

簡単な代入方法2

次の項目に移る前に、配列の使い方をもう一点紹介しておきます。

入門の最初の方で示したとおり、TJSは自由な書式で書く事が出来ます。 よって、最小単位(トークン)さえ崩さなければ、どのように書いても構わないのです。 これを利用して、配列を更に読みやすく記述できます。

var Yamada =
[
  "山田", ←0番目の要素
  100,    ←1番目の要素
  90,     ←2番目の要素
  88,     ←3番目の要素
  95      ←4番目の要素
];//セミコロンを忘れないように注意
				

ここで、配列の初期化のおさらいをしておきましょう。 ご自分の使いやすい書き方で書いて構いませんから、全てを覚える必要はありません。

//配列の初期化(要素の数は0)
var dim = new Array();
				
//配列の初期化(要素の数は0)
var dim = [];
				
//配列の初期化と代入
var dim = new Array();
dim[0] = 100;
dim[1] = 5;
:
				
//配列の初期化と代入
var dim = ["山田",100,5];
				
//配列の初期化と代入
var dim = [
  "山田",
  100,
  5
];
				

二次元配列

変数にはどのようなデータも入れる事が出来る、と以前に書いたとおり、 何でも入れる事が出来ましたね。例えば、そう、オブジェクト(実物)であっても。 今回は、これを利用して前回のプログラムを改良します。

訂正するのはただ一つ。全ての要素をfor文などの反復文を利用して表示させる事。 よって、全データを配列としてしまうのです。 簡単に言えば、配列の要素の中に配列を作ってしまえば良いのです。

配列の配列
//配列に配列を入れる
var dim = [
  ["山田",100,5],
  ["田中",90,88]
];
				

今回は、以前の配列を管理する為の配列として「pupil」を用意します。 その構成は以下のようにします。

//生徒データを管理する配列
var pupil = [
  各要素の種類を指す配列……箱番号0
  山田くんの情報配列……箱番号1
  斉藤くんの情報配列……箱番号2
  田中くんの情報配列……箱番号3
];
				

実際に改良したプログラムが以下になります。

var result="";
var pupil =[
    ["名前","国語","算数","理科","社会"],//箱番号0=pupil[0]
    ["山田",100,90,88,95],//箱番号1=pupil[1]
    ["斉藤",65,75,80,99],//箱番号2=pupil[2]
    ["田中",90,92,74,100]//箱番号3=pupil[3]
];

//配列pupilに対して全て
for ( var i=0; i<pupil.count; i++)
{
    //各要素に入っている配列に対して全て
    for ( var j=0; j<pupil[i].count; j++)
    {
        result += pupil[i][j]+"\t";
    }
    result += "\n";
}
System.inform(result);
				

前回のプログラムと比べても、随分すっきりしましたね。 おまけに、修正する箇所は配列pupilの要素だけで終わります。

簡単に解説しておきます

(1)配列の中の配列を指す

配列の要素の中の配列を指すには、もう一つ[]を追加して書きます。 配列pupilの箱番号1に入っている配列を指すにはpupil[1]と書きますよね。 更に、その中にある配列の箱番号2を指すなら

pupil[1][2]

と書きます。今回は山田君の点数90が得られます。

配列の中の配列の箱
(2)多重ループを使う

for文の中にfor文を書く使い方は以前にもやりましたが、覚えているでしょうか? 最初のfor文の条件は「pupil.count」が変数iより低ければ、とあります。 よって、変数iがpupilの箱の数になるまで繰り返す事を意味します。

二つ目のfor文は「pupil[i].count」が変数jより低ければ、とあります。 これも同じ考え方で、pupilの箱番号(ここでは変数iを指す)に入っている配列を操作します。 よって、変数jがpupil[i]に入っている配列の箱の数になるまで繰り返す事を意味します。

このように、配列は単体で使用するよりも、 配列の中に配列を入れて操作するほうが、用途は更に広がります。

配列の中に配列を入れる形を配列の配列、または多次元配列と呼ぶ事があります。 また、配列の中に配列を入れた形を特に二次元配列と呼び、配列の使い方の基本となります。

二次元配列の要素の中に更に配列を入れる事も出来ます。

配列→配列→配列……配列[番号][番号][番号]

この形を三次元配列と呼び、同様に四次元、五次元…といくつでも作れます。 が、あまり使われることはありませんので、頭の片隅にでも入れておけば大丈夫です。

少しややこしいのですが、 配列という箱のイメージがしっかり出来れば、 必ず理解できると思うので、諦めずに何度も読み返して理解してください。 多次元配列と多重ループの組み合わせは、ゲームなどを作る際によく利用される手法です。

2D迷路を作る

最後に二次元配列を使用して簡単な迷路を作ります。 まだ画像を扱うことは出来ませんので、 壁を「*」で、歩ける場所を「-」で表現します。

2D迷路

あまり広げても仕方ないので縦×7、横×15の小さな迷路にします。

var result="";

//迷路のデータ
var maze =[
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,1,1,1,1,1,0,1,1,1,1,1,0,0,0],
    [0,1,0,0,0,1,1,1,0,0,0,1,1,1,0],
    [0,1,1,1,0,0,0,0,0,1,1,0,0,1,0],
    [0,1,0,1,1,1,1,1,0,0,1,1,0,1,0],
    [0,1,0,1,1,0,0,1,1,1,1,1,1,1,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
];

for ( var i=0; i<maze.count; i++)
{
    for ( var j=0; j<maze[i].count; j++)
    {
        result += ( !maze[i][j] ) ? "*" : "-";
    }
    result += "\n";
}
System.inform( result );
				

迷路のデータは二次元配列mazeに入れていますが、じ~っと眺めていると、迷路みたいに見えてきますよね? (0が壁、1が通路として見ると……。)

これを利用して、もっと複雑な迷路に仕上げてみてください。 縦幅や横幅を広げ、大きなマップにするのもいいですね。 今回の手法は様々なゲームに応用できるはずですから、是非是非、お試しください。