配列 - JavaScript プログラミング
PROGRAM
配列を使ってみる。
目次
- 配列とは … 概要を捕らえるためのお話です。
- 配列を操作する … 使う上で知っておかなくてはいけないお話です。
- 二次元配列を組み立てる … 配列の中に配列を、というお話です。
- 関数とのやり取り … 配列変数を関数とやり取りする上での注意事項です。
- 切り出したり連結したり … 配列をいろいろと切り貼りしたり、並び替えたりするお話です。
- スタックとしても使用できる … 配列をスタックのように利用する機能が用意されていました。
- Mac でスタックらしい処理をするには … Mac でスタック風メソッドを使いたい場合はどうするかというお話です。
配列とは
数学をかじったことがある人なら小耳に挟んだ事があると思いますけど、プログラミングにおいても配列を使用して、ひとつの名前 (変数名) に複数の値を格納することが出来ます。
もっとも数学の配列ほど自由度はないですけど、それでも逆にシンプルで、非常に有用です。これを上手に使えるようになると、プログラミングの幅が広がって、より難しいものも作れるようになります。
前置きが長くなりましたけど、配列とは、ひとつの変数に値を番号付けして格納する事が出来る、といった感じでしょうか。
たとえば…、よくアンケートとかでありますよね。
- 氏名
- 住所
- 年齢
- 気に入ったものの題名
- どこが気に入ったか
こんな内容のアンケートの内容を変数に保存したいというようなときこそ、配列の出番です。たとえば answer という名前の配列変数を用意してそこへ格納してあげれば、常にまとめて取り扱えますので、「あれれ、"住所" はどこへいったかな…」 といった事態に陥らずに済みます。2番目の内容を見ればいいのですから。
これだけだとどんなものか解りにくいかもしれませんけど、次で具体的に使用しながら、配列とはどういう使い方をするのかを見て行きましょう。
配列を操作する
JavaScript で配列を使用するには Array オブジェクトを作成する必要があります。たとえば、10 個の値を保存できる配列 Arr を作成する場合は次のようにします。
// 0 から 9 までの 10 個の配列を作成する
var Arr = new Array(10);
また、これらの配列の値を設定したり取得したりする場合は、0 から始まる番号を、変数名の後ろに "[ ]" で付けてあげます。
// 6番目(0から始まるので5)の要素に値を代入する。
Arr[5] = "test";
// 3番目の値を1番目に代入する。
Arr[0] = Arr[2];
上記ではしっかりと要素数を指定しましたが、これ以上の、今回の例では10番目以降に値を代入しても、JavaScript では自動的に大きさを調整してくれます。
// 20番目(10番目以降)の要素に値を代入。
Arr[19] = "over";
このようにすると、要素数が自動的に 20 にまで拡張されます。実際に拡張されたかどうかは、配列変数が持っている length プロパティを参照することで確認する事が出来ます。
たとえば HTML ファイル中の JavaScript で確認してみる場合は、次のような HTML ファイルを用意して開いてみましょう。
<html>
<head></head>
<body>
<script language="JavaScript">
// とりあえず10個の値を保存できる配列の準備
var Arr = new Array(10);
// その状態での配列サイズを表示
document.write("拡張前:" + Arr.length + "<br>");
// 20番目(インデックスでは19)に値を代入
Arr[19] = "test";
// 配列サイズが拡張されたかの確認
document.write("拡張後:" + Arr.length + "<br>");
</script>
</body>
</html>
これで、実際に容量が拡張されている (と、少なくとも配列は理解している) ことがわかるでしょう。このような性格の配列なので、作成時にわざわざ要素数を指定しないで配列を作成することも出来ます。その場合は単純に、次のようにします。
// 配列を作成する
var Arr = new Array();
これで、要素数 0 の配列が完成します。この配列でも自動拡張は健在ですから、上記までの10個の配列とまったく同じ感覚で使用する事ができます。ですので、配列の個数に特にこだわりが無い場合はこちらを利用すると楽かもしれません。
逆に要素数を指定する場合には、たとえば 10 個までしか格納しない、など、こだわりがある場合に使用するのがいいでしょう。
もっとも、何も手を加えなければそれ以上の値を保存できてしまうので、保存する要素番号と length プロパティの値を比べて、まだ許容範囲ないならば代入する、といった手間が必要になりますけど…。
二次元配列を組み立てる
上のように、1つのアンケートの回答…、いやアンケートでなくても他にも1つのお財布の中に ”千円札が何枚、1万円札が何枚…” など、1つの物に関して番号付けをして値を保存する配列を 「1次元配列」 と呼びます。
呼び方はどうでもいいのですけど、時にはそれをさらにまとめて管理したい事があると思います。
たとえば、上で挙げたアンケートのお話ですけど、回答のうちの1つに注目しているときは一次元配列で問題ないですけど、アンケートというのはたくさん集まるものですよね。
「… このアンケート結果を、全部まとめて管理したいな」、という時に、二次元配列という考え方が生きてきます。
二次元配列というのは、配列をさらに番号付けする、といった感じでしょうか…。
これを使うと、まとめたものをさらにまとめて管理する事ができます。"次元" という言葉は、"集合" という感じで捕らえてもいいかもしれません。アンケートの回答という集合と、そのアンケート回答用紙の集合、その二つを管理したいから、二次元配列を使うのです。
たとえばアンケートについて、10個の回答が得られたとしましょう。
1枚目のアンケート回答者の 年齢を知りたければ、アンケートデータの配列の、1番目の中のさらにその3番目を…。と、文字で表現してしまうとなんだか難しくなってしまいますね。今回の例の具体例を挙げると、次のようになると思います。
今回は保存する個数が決まっていることもあり、また、実際に組んでみないと解らないかもしれませんけど、二次元配列を個数を決めずにプログラムするのは何かと面倒なので、話を簡単にするために、要素数を指定する形で話を進めていきます。
配列名は answer としましょう。二次元配列の作成は、まずは普通の (一次元) 配列を作成し、そのそれぞれの要素の値としてさらに配列を作成するという感じになります。
// まずは配列を普通に作成します。今回は 10 個です。
var answer = new Array(10);
// 続いて、これらの要素すべてに、回答内容を保存するための配列を作成します。
// length を利用しているため、10 個以上の配列でも正常に初期化(準備)できます。
for (i = 0; i < answer.length; i++)
{
// それぞれに、回答の5項目を保存できるように配列を作成します。
answer[i] = new Array(5);
}
これで準備完了です。
ここまで出来てしまえば、あとは使うのは非常に簡単です。一次元配列では "[ ]" ひとつで指定したところを、二次元配列では "[ ][ ]" とふたつで要素を指定すればいいのです。
つまり、1枚目のアンケート回答者の年齢を知りたければ、年齢は3番目だから、次のように参照すれば、該当するものが取得できます。
// 1枚目の3番目(配列の要素番号では0の2)を参照する。
answer[0][2]
上記は不完全な構文ですけど、このような指定で document.write に渡して表示してみたり、または代入文の左辺に書いて代入してみたり、普通の変数のように利用する事が可能です。
なお、慣れないと理解しにくいかもしれませんけど、answer[0] 自体も配列ですので、answer[0].length といった、一般配列と同じような操作が可能です。
このように二次元配列にする利点は、単純にひとつの変数にデータをまとめるだけでなく、他にも大きな利点があります。たとえば、アンケートに回答してくれた人の平均年齢を計算したいときに、for ループを使って簡単に計算する事が可能となります。
// 回答者の平均年齢を計算する。
// とりあえず使用する変数を宣言しておきます。(しなくても平気ですけど。)
// sum には年齢の合計が、avg には年齢の平均が、あとで計算して代入します。
var sum;
var avg;
// 回答者数を count に保存します。今回は、回答者数は配列の最初(一次元)の要素数である 10 とします。これに満たない状況が考えられる場合は、それを配慮したつくりにしないといけませんが、気をつけるところはこの count の設定部分だけです。これさえ適切なものが設定できれば、あとは下記の通りのスクリプトで平均が計算できると思います。
var count = answer.length;
// for ループを使って、年齢(3番目)の合計を sum に保存して、平均を avg に保存します。
sum = 0;
for (i = 0; i < count; i++)
{
// 10 個すべての年齢 (answer[*][2]) を sum に足し合わせます。
sum = sum + answer[i][2];
}
// 足し合わせた年齢を個数で割れば、平均を計算できます。
avg = sum / count;
細かいところの注意書きで複雑に見えてしまうかもしれないですけど、count の値を決定してしまった後は、非常に簡単に、すっきりとプログラムがまとまっている事がわかるでしょうか…。
まとまっているのも然ることながら、特に重要なのがアンケートの回答が 10 個以上になったとしても、count の値さえちゃんと調整すれば、何事も無かったかのように正常に計算処理を行う事が出来るということです。今はまだ解らなかったとしても、プログラムをたくさん組むようになるとこの重要性が身をもって理解できるようになると思います。
このような感じで、空間においても2次元から3次元、4次元空間になるにしたがって難しくなってくるように、プログラミングにおいても次元が増えるとなかなか理解するのが大変ですけど、2次元配列を理解できればそうとうプログラムに強くなります 。
ですので興味が持てたら、がんばって理解してしまいましょう。
切り出したり連結したり
JavaScript の配列には、その内容をいろいろと切り出したり並び替えたりといった機能が備わっています。ここではそれらについて見てみることにしましょう。
下準備
見ていくにあたって次の配列を用意しておくことにします。
// 1月から12月までの英語名を保存した配列(作成と値代入を別々に)
// 今回のお話の都合上、今回は2つの配列に分けてあります。
var LIST_A = new Array(12);
var LIST_B = new Array(12);
// LIST_A には1月から6月までを保存します。
LIST_A[0] = "January";
LIST_A[1] = "February";
LIST_A[2] = "March";
LIST_A[3] = "April";
LIST_A[4] = "May";
LIST_A[5] = "June";
// LIST_B には7月から12月までを保存します。
LIST_B[6] = "July";
LIST_B[7] = "August";
LIST_B[8] = "September";
LIST_B[9] = "October";
LIST_B[10] = "November";
LIST_B[11] = "December";
なんとも長々と書いてしまいましたけど、LIST_A と LIST_B という名前の配列を用意して、そこへ1月から順番に月名を代入してあります。切ったり貼ったりを見ていく都合上、あえて2つの配列に分けて値を保存してあります。
配列を作成する際に、保持する値を渡すという方法もありますので、上記のような書き方が好きではない場合は次のような感じで1行にまとめてしまうこともできます。どちらでも同じことなので、状況やお好みで選んでください。
// 1月から12月までの英語名を保存した配列(作成時に値設定を同時に:その1)
var LIST_A = new Array("January", "February", "March", "April", "May", "June");
var LIST_B = new Array("July", "August", "September", "October", "November", "December");
または…、ちょっとしつこいですけど、JavaScript は途中で改行しても基本的には問題のないで、次のようにして見やすくすることもできます。個人的には、(プログラム的に) 1行でまとめる場合は、次のように適度に改行を挟むことが多いです。
// 1月から12月までの英語名を保存した配列(作成時に値設定を同時に:その2)
var LIST_A = new Array
(
"January",
"February",
"March",
"April",
"May",
"June"
);
var LIST_B = new Array
(
"July",
"August",
"September",
"October",
"November",
"December"
);
最初に挙げた準備とあまり違わないように見えるかもしれませんけど、慣れてくると、それぞれでどちらがふさわしいかを考えながら使えるようになると思います。違いがわからないうちは、お好みで使い分けてください。
なお、上記の注意点としては、あくまでも ”本来1行” であるものを、見やすくなるように改行を入れただけということを忘れないようにしましょう。流れるままに "June" や "December" の後ろにカンマ ( , ) をつけると構文エラーになります。
連結してみる
さっそく、配列の持っている機能の一つの連結を行ってみようと思います。このためにわざわざ6つずつに分けたようなものですから^^; 連結には配列のもっている concat というメソッドを使用します。
newArray = Array_1.concat(Array_2);
構文としては、連結元の配列 Array_1 の concat メソッドに、連結したい配列 Array_2 を渡してあげると、それぞれが連結された配列 newArray を取得することができるようになっています。
注意する点としては、"Array_1 の値に Array_2 が連結される" のではなくて、"Array_1 の値に Array_2 が連結された値を取得できる" といった感じでしょうか…。解りにくいかもしれないですけど、要は、Array_1 が保存している値にはなんら影響が無いのです。
さて、下準備しておいた配列を使って連結を行う場合はどのようになるかを見てみましょう。配列 LIST を用意して、その中へ連結したもの、すなわち1月から12月までのすべてがそろった値を設定してみます。
// LIST_A と LIST_B を連結して、その結果を LIST へ保存する。
LIST = LIST_A.concat(LIST_B);
これで LIST にすべての月が保存されます。重ねていいますが、LIST へ代入せずに LIST_A.concat(LIST_B) だけでは、LIST_A の値に変化がないため、事実上連結されないので気をつけましょう。
表示してみる
これで LIST に、実際にすべての月が格納されたかどうか確認してみましょう。
確認の仕方にはいくつかありますが、一番お勧めなのが、join というメソッドを使用する方法です。
str = LIST.join(",");
これは LIST に保存されている値すべてを、join に渡した文字列で区切ったものを取得するというものです。これで普通のひとつの文字列となるわけですから、簡単に表示したりすることができます。
<html>
<head></head>
<body>
<script language="JavaScript">
// 普通の文字列として str へ連結してから、それを document.write で表示しています。
var str = LIST.join(",");
document.write(str);
</script>
</body>
</html>
上記の例では LIST の初期化 (月名の代入) は省いていますので気をつけてください。上で join に渡している "," の部分をいろいろかえることで、好きな文字列で区切ることができるので、なにかと便利です。
この join というメソッドは、ブラウザで利用する場合は Internet Explorer, Netscape Navigator ともに、バージョン 4.0 から利用できるようです。
これを利用して普通の文字列に変換してから表示なりをするのが一番安全だと思うのですけど、他にもいくつか、配列のないようすべてを確認する方法があるので、紹介しておきますね。
単純なのが、配列そのものを何食わぬ顔をして渡してしまうという方法です。
<html>
<head></head>
<body>
<script language="JavaScript">
// 配列をそのまま、document.write で表示しています。
document.write(LIST);
</script>
</body>
</html>
これでも上記の join の時と同じように、カンマ区切りで連結できてしまいます。ただ、ちょっとしたテストとかにならいいと思いますけど、ちゃんとしたスクリプトを組むときには面倒くさがらずに join を使うのがお勧めな気がします。
理由は単純に、”曖昧だから” です。
それともうひとつ、toString というメソッドを使う方法もあります。
<html>
<head></head>
<body>
<script language="JavaScript">
// 配列を toString() で文字列化して、document.write で表示しています。
document.write(LIST.toString());
</script>
</body>
</html>
これまた (^^;) 上記の join の時と同じ結果が得られることとなると思います。これも自分の中では、内容確認などのテスト用に使用する感じだと思っています。
というのも、”文字列化せよ” との命令を toString() というメソッドで明示しているので配列名だけを渡すよりはずっといいのですけど、やはり、どう文字列に変換するか、といった情報がないので、スクリプトを実行してくれる環境に左右されてしまう可能性がある、というのが理由です。
また、この toString() メソッドは Internet Explorer 4.0 では使えないとのことです。自分の確認した環境、Internet Explorer 6.0 では問題なく動作しましたが、念のためそのあたりも気にして使用するといいかもしれません。
部分的に抜き出してみる
使う機会がどれほどあるか疑問ですけど、配列には保存されている値の一部分を切り抜く機能が用意されています。それに使用するメソッドが、slice です。
partArray = Array.slice(idx_start, idx_end);
これを利用することで、配列 Array を切り抜いて、配列 partArray に保存することができます。このとき、切り出した配列はまた、0から始まる番号が付けられるので気をつけましょう。
また、曖昧な表現でなんですけど、切り取られる配列は、idx_start で指定した要素番号から、idx_end で指定された要素番号の "ひとつ手前" までとなるようです。ですので番号を指定するときには混乱しがちですけど、切り取り始める最初のインデックス番号と、切り取られずに済む最初のインデックス番号を指定する感じになります。
たとえば、配列の中から4番目から6番目までを切り出したいときには、次のようにします。
// LIST の4番目から6番目までを切り出して、LIST_PART に保存します。
LIST_PART = LIST.slice(3, 6);
配列の番号指定は、0から始まるので注意してください。これで LIST_PART には、"April", "May", "June" の 3 つの値が保存されます。切り出したあとの配列はまた 0 からの番号付けがなされるので、LIST_PART[0] に "April" が保存されていることに注意です。
また、ごく自然なことですけど、配列の先頭から 3 つ取り出したいようなときは次のようにします。こういった使い方なら、もしかすると利用する機会も多いかもしれません。
// LIST の先頭から 3 つを取り出して、LIST_PART に保存します。
LIST_PART = LIST.slice(0, 3);
最後の3個を取り出す場合は、length で配列の個数が取得できることを利用して次のようにします。
// LIST の最後から 3 つを取り出して、LIST_PART に保存します。
LIST_PART = LIST.slice(LIST.length - 3, LIST.length);
/*
LIST.length で、配列 LIST の要素数が取得できることに注目します。ここでは 12 です。
開始位置は0から始まる要素番号。
最後の3つですから、最終位置である (LIST.length - 1) から 3 を引いて 1 を足します。すなわち単純に (LIST.length - 3) です。
終了位置は、最終位置である (LIST.length - 1) の次 (+1) です。すなわち単純に (LIST.length) です。
-- ひとくちメモ(代数学)--
『個数の求め方』
最後の番号から、最初の番号を引いて、1を足します。
例:Array[10] から Array[13] までに存在する要素数は、
【解】13 - 10 + 1 で、合計 4 つ。
【実例】Array[10], Arrya[11], Arrya[12], Array[13]
個数と最後の要素数を求めるにはこれを変形すると…。
(与式) =
【個数】=【最後の番号】-【最初の番号】+ 1
両辺から【個数】を引いて…
0 =【最後の番号】-【最初の番号】-【個数】+ 1
両辺に【最初の番号】を足して…
【最初の番号】=【最後の番号】-【個数】+ 1
------------------
… などと、自分自身が書いててよくわからなくなったので、嘘を言っていないかどうか証明してみました^^;
*/
並び替えてみる
配列に保存されている値を並び替えるには、標準では、2つのものが用意されています。
そのうちのひとつが、保存されている要素を逆順に並び替えるというものです。これには reverse というメソッドを利用します。
Array.reverse();
このメソッドを利用することによって、配列 Array の要素番号が、ちょうど逆に振られます。注意点としては、今までの slice や concat メソッドと違って、reverse を実行した配列そのものの情報が書き換わることでしょうか。
たとえば、今までの1月から12月までの情報を、逆順に、すなわち、12月から1月までに並べ替えたい場合は次のようにします。
// LIST の並び順を逆順にします。
LIST.reverse();
これで、たとえば LIST[0] の内容を見てみると、"December" となっていることがわかります。ちなみにもういっかい、LIST.reverse() とすれば元に戻ります^^
もうひとつ、標準で用意されているものが、sort メソッドです。
Array.sort(sort_func);
これを利用すると、意図的な条件の下、内容を並べ替えることができます。sort_func のところで並び替えの条件を指定することができるのですけど、省略すると "文字コード順" となります。
文字コードというところが少し厄介ですけど、少なくともアルファベットならばアルファベット順に並びます。ただし、大文字が優先されるため、大文字がアルファベット順に並んだあと、小文字がアルファベット順に並ぶ、といった感じになります。
// LIST の並び順を、文字コード順にします。
LIST.sort();
こうすることで、"April", "August", "December", "February", "January", "July", "June", "March", "May", "Nobember", "October", "September" というような並びとなります。
をご覧ください。16進数に慣れていないと解りにくいかもしれませんけど、左上から右下へ行くにしたがって、順位が高い (sort 時に後ろへ回る) ものとなります。なお、16進数とは、0〜15を一桁として扱った数字です。パソコン内部にとっては0〜9よりも何かと便利なのです。
さて sort では、意図的な条件を指定することが可能となっています。
その方法は、sort メソッドの引数として、条件を示す関数を渡すというものです。関数の書き方については別の機会に細かく述べることとしますけど、2つの引数をとって、その2つにおいての並び順はどうなっているかを返す関数を渡します。
result SORT_FUNC(a, b);
用意する関数の構造としては上のような感じです。a と b の二つの値をもらって、それの順位がどうかを result で返します。その戻す値の意味合いは次のようになります。
正の整数 ( > 0 ) | b の次に a がくるようにしたい場合、すなわち b < a |
---|---|
ゼロ ( = 0 ) | a も b も同等の場合、すなわち a = b |
負の整数 ( < 0 ) | a の次に b がくるようにしたい場合、すなわち a < b |
このような適切な条件を示してあげれば、あとは sort メソッドが自動的に、配列の隣り合う各要素について条件が成り立つように並び替えてくれます。
ではためしに、文字コードの逆順に並べるような sort を実験してみましょう。
// 文字コードの逆順を判定するための関数を用意します。
function sort_func(a, b)
{
// ここでは、変数 result に結果を保存するように組んでいます。
var result;
// ">" や "<" といった演算子は、数字なら数字の大きさで、文字ならば文字コードで判定されます。
if (a > b)
{
// a の方が大きいときは a を前に置きたいので、マイナスの値を返します。
result = -1;
}
else if (a < b)
{
// 逆に a の方が小さいときは b を前に置きたいので、プラスの値を返します。
result = 1;
}
else
{
// それ以外、すなわち a も b も同等の時には、0 を返します。
result = 0;
}
// result 変数に保存した値を返します。
return result;
}
// LIST の並び順を、文字コードの逆順にします。
LIST.sort(sort_func);
このように関数をひとつ作る手間さえかければ、上記の逆順以外にも、投票ポイント順など、いろいろと並べ替えを行うことができます。
なお、今までの苦労に少し水を差す感じになってしまいますけど、上記の例のような文字コードの逆順に並び替える場合は、次のような方法もあります。
// LIST の並び順を、文字コードの逆順にします。(応用編)
LIST.sort();
LIST.reverse();
まず小さい順にならべて、そのあと逆順に並べなおせば、完成です^^;
せっかくなので余談をもうひとつ…。内容をてきとうに並べ替えたい場合も、次の関数を使って sort を行えば実現できます。
// 配列をてきとうに並べるための関数
function sort_random(a, b)
{
// てきとうなので a と b は判断材料に使用しません。
return ((Math.floor(Math.random() * 3)) - 1);
}
やっていることは、Math.random() * 3 で取得した、0 以上 3 未満の擬似乱数 (てきとうな数字) を取得して、それは小数を含むので Math.floor() で整数化、この段階で 0 〜 2 までの値となったので、1 を引いてプラスかマイナスか、ゼロか、を示すようにしています。
プラスかマイナスか、乱数の影響でその都度てきとうに選択されることになりますので、これで並び替えのたびに、てきとうなならびになってくれます。
これ以外にも、sort の機能を使わずに、自らのプログラムで並び替えることも、当たり前ですけどできます。
これを利用するときというと、例外を含むなどの一定な条件がない場合、一部分のみを交換したい場合などが考えられます。たとえば、3番目と8番目を交換したいという場合には次のようにします。
// LIST の3番目と8番目を交換する。
var temp;
// まずは8番目を一時的に別の変数へ保存します。
temp = LIST[7];
// つづいて、3番目の値で8番目を上書きします。
LIST[7] = LIST[2];
// 最後に、とっておいた8番目の値を3番目へ書き込みます。
LIST[2] = temp;
このような感じで、別の変数をひとつ使ってあげないといけないのですけど、これで交換することができました。あとは条件文なりをつけていろいろと調整してあげれば、思い通りの並び替えが可能です。
スタックとしても使用できる
スタックとは、積み木を重ねるように、値を重ねて保存していく入れ物のような感じです。
上乗せしていく感じで値を保存するので、値を取り出すときにも上から順番に、つまり最後に乗せたものからしか取り出せない、というのが本来のスタックです。JavaScript では配列を応用するため、途中から取り出せたりとかも平気でするんですけどね。それは、今回は気にしないで行きましょう。
※ ここのお話は Macintosh の MacOS X 版 IE 5.2 では利用できないことが判りました。Mac 環境においては、これらのメソッドを使用すると、その時点でスクリプト処理が打ち切られる場合があるので気をつけましょう。
配列のようにいくつも値を入れていけるものの、取り出すときは最後から…、配列と違って融通が利かないため、あまり利用価値がなさそうに思えるかもしれないですけど、これが便利だったりすることもあるのです。
ではどこで使うのか、と聞かれるとなかなか一口で説明をするのは難しいのですけど…。
とりあえずここでは、そういう話は機会があったらということにして…、とさせていただきますね。なにはともあれ、実際にどんな機能として用意されているかを見てみましょう。
stack.push(data);
data = stack.pop();
基本的にはこの2つのメソッドを使います。
空の配列を用意したら、push メソッドを使って値を順次のせていって、それを最後にのせたものから pop メソッドによって取り出すという感じの操作方法になります。
スタックが空であるかは、JavaScript では length プロパティを参照して、配列の要素が 0 かどうかで判断します。
ではさっそく、いままでの例題どおりの、月名を示す文字列をスタックで取り扱うとどんな感じになるか、見て行きましょう。
// 1月から12月までの英語名をスタックに保存します。
// スタックの機能は配列の一部として実装されているので、まずは空の配列を作成します。
var STACK = new Array();
// 1月から12月までを、push メソッドを使って配列(スタック)へ保存します。
STACK.push("January");
STACK.push("February");
STACK.push("March");
STACK.push("April");
STACK.push("May");
STACK.push("June");
STACK.push("July");
STACK.push("August");
STACK.push("September");
STACK.push("October");
STACK.push("November");
STACK.push("December");
ここまでが値の設定です。
今回の月名のようなものには少し合わないですけど、このとおり、配列の要素番号がプログラムに現れてこないので何かと便利だったりします。
たとえば途中に値を入れておきたいとき、わざわざそれ以降の要素番号を変えずに済みます。それだけではなく、純粋にもうひとつ要素を追加したい場合にも、何番目まで値が保存されているかを調べずに済むのでいろいろと勝手がいいです。
push を使ったからといって、JavaScript では配列であることに変わりないですから、途中から値を取り出せなくなるようなことはありません。ですので、配列の値を初期化 (準備) するときなどに積極的に活用しましょう。
…、と話が少し脱線してしまったのですけど、この push で保存した値は、スタックでは pop を使って取り出します。
ただし、pop メソッドを使うと、スタックに最後に保存した値、すなわち配列の一番最後の値が取得できると同時に、その最後の値が配列から消去されるので気をつけてください。
下の例は、スタックに保存されている値を順次取り出して、Web ページに表示する HTML の例です。
<html>
<head></head>
<body>
<script language="JavaScript">
// スタックが空になるまで、すなわち、配列の要素数が 0 になるまで、繰り返して内容を表示します。
while (STACK.length > 0)
{
// pop で取り出した値を data に保存しています。このとき、STACK の最後の要素は削除されているため、STACK.length の値もつられてひとつ減ります。
data = STACK.pop();
// とりだした data を画面に表示しています。"<br>\n" は単純に表示に改行を挟んでいるだけです。
document.write(data + "<br>\n");
// ここは while ループ内ですので、条件を満たすまで、すなわち STACK が空になるまで、上記の処理が繰り返されます。
}
</script>
</body>
</html>
これを実行すると、push で保存したときとは逆順、すなわち "December" から "November", "October" というように、保存してあった値が表示されます。
スタックとは違ってきてしまうのですけど、これに似たような機能があるので、それもここでさらっと紹介しておきますね。
data = Array.shift();
Array.unshift(data);
これは、先ほどのスタックとは対照的に、配列の先頭要素を操作するためのメソッドです。
shift メソッドは、その配列の先頭要素を削除してしまうものです。削除後、その後ろの要素をひとつずつ前にずらします。たとえば上記の例の配列で LIST.shift() を実行すると、先頭の "January" が削除されて、"February" から始まる配列になります。
逆に unshift メソッドは、引数に与えられた値を配列の先頭要素に追加して、それ以降をひとつ後ろへずらします。ひとつにかぎらず、カンマ区切りで複数個の値を与えると、その分だけずらしてくれるようになっています。
これと先ほどのスタックを応用すると、キューとしても利用することができます。
キューというのは、日本語で言うと待ち行列、ちょうど行列の順番待ちみたいな感じでしょうか。先に並んだ人から先に列から出て行く、つまり、先に保存した値から先に取り出していくというものです。
やりかたとしては、push メソッドをつかって最後尾へ値を追加して行き、とりだすときは shift メソッドを使って先頭から取り出してあげます。pop が shift になるだけなので、(この拙い文書で) スタックが理解していただけていれば、こちらも特に難しいことはないと思います。
Mac でスタックらしい処理をするには
配列をあたかもスタックのように操作できるメソッドがあるおかげで配列を操作するのがかなり楽なのですが…、ある日 Macintosh にて JavaScript が正常に動作するかを確認したところ、エラーどころか、処理が途中で打ち切られてしまう問題に遭遇しました。
原因はまさに、配列処理で unshift や push を使っていたというものでした。確認した環境は Mac OS X 10.3 の IE 5.2 なのですけど、これらの処理にたどり着いたとき、スクリプトが何の報告もなく終了してしまうようです。
そこで今回は、これらスタックらしい処理を行いたいけれど、Macintosh では使えない。ならば、それを使わずに記述するにはどうしたらいいか。そういった視点でお話を進めてみることにします。ただし、pop と shift に関しては、要素を切り詰める良い方法が思いつかなかったため、ここでは push と unshift に関してのお話だけとなります。
stack.push(data);
push メソッドは、配列の最後に指定した要素を追加するためのメソッドです。これを実現するためには length プロパティから、その配列がいくつの要素を持っているかを調べて、適切な位置に新しいデータを追加すれば大丈夫です。
// 擬似 push 処理
stack[stack.length] = data;
配列のインデックスが 0 から始まるため、要素数を示す length の値をそのまま使用してやることで、配列の最後に値を保存することが出来ます。配列サイズは自動的に拡張されるため、もう一度これを実行すればさらに後ろに追加されていくことになります。
stack.unshift(data);
unshift メソッドは、配列の先頭に指定した要素を追加するためのメソッドです。これを実現するためには保持している値をひとつずつ繰り上げ、最後に先頭に新しい値を代入する感じになります。
// 擬似 unshift 処理
var i;
for (i = stack.length; i > 0; i--)
{
stack[i] = stack[i - 1];
}
stack[0] = data;
保存されている値全てを for ループを使ってひとつずつ後ろへずらします。そうすると [0] 番目が意味を成さない [1] から始まる配列となるので、あとは [0] 番目に追加したい値を保存してあげれば、事実上、先頭に値が追加された配列が出来上がります。
[ もどる ]