変数がいっぱい
今回は「配列変数」の話をします。
配列変数は「HSP以外の言語でも当たり前のように出現する重要なもの」ですので、是非とも覚えましょう。

「配列変数」とは、同じデータ型を並べた変数の塊のことです。
データ型は、変数の扱える値のタイプ(数値や文字等)や、表現範囲が決められた、まさに「型」のことで、
HSP3では、数値型・実数型・文字列型・ラベル型・COMオブジェクト型と言ったものが用意されており、
同じ型が連続して幾つも使える変数群が●●型配列変数です(●●には上記の型名が入ります)。
…と、言葉の説明だけでは少し内容が難しいので噛み砕いてみましょう。

変数には、1つの値や文字列等だけを入れることができましたね。
「1」という値を入れたり「ABC」という文字列を入れたり…。
もし「-5」「100」の2つを同時に持たせたいなら、2つの変数、例えば変数Aと変数Bを使う必要があります。
10個を同時に使う場合は変数10個、100個なら変数100個…と、その数分の変数が必要です。
ツール系ソフトでは、同時に大量の変数を扱うことは少ないかもしれませんが、
ゲームではパズルやアクション等のジャンルを問わず、幾つもの値を同時に保持しなければなりません。
HSP2講座同様にイメージしやすく、膨大な数を保持するシューティングゲームを例にしてみます。
自機を縦横自在に動けるようにするには、X座標・Y座標の2つの情報を保持することになります。
自機だけしか出現しないシューティングゲームはほとんど見ませんね。
敵も画面上に出現し、自機とは別の位置に出現して異なる動きをしますので、
敵用にもX座標・Y座標の2つが必要になります。
敵は同時に複数出現することがありますので、敵数×2が必要になります。
シューティングと言うくらいですので、弾で敵を打ち落とす(シュート)行為が出てきます。
弾も敵に当たるまで、位置情報を保持している必要があり、複数の弾が必要なら複数の変数が必要です。
…さて、コレだけで幾つの変数が必要になったでしょうか?
変数jiki_xjiki_ytama_x1tama_y1teki_x1teki_y1tama_x2tama_y2teki_x2teki_y2…
弾や敵など複数同時に発生した場合、全く同じ位置・同じ動きはないにしろ、
特に弾なんかはある程度同じ動きをするということがよくあります。
それなのに別々の変数である為に、前の章で覚えていただいた繰り返し命令が全く役に立ちません。
全部の弾に対して同じ様に値を入れていかなければならないから、です。
現在使われているのがどれで、今回操作するのがどの変数か、管理が非常に難しくなりますね。
長々と説明しましたが、こういったときに役立つのが配列変数なのです。

配列変数は、数値と文字列を混合することはできませんが幾つもの値を保持することが可能です。
数値は数値でも、整数と実数は別物ですので混ぜられません。
「1つに複数保持できる」と言うのは少し語弊がある為に補足しておきますが、
厳密には1つの配列変数には複数の変数が入ったイメージで、
配列変数内のそれぞれの入物「要素」には1つの値しか保持できませんが、
1つの配列変数としては複数の値を保持できると言うことです。

普通の変数は、上記のような家1つ分で1つの家族が住んでいるのと同じ考えです。
「●県●市●番地●号」という住所を指定すると、荷物をその家に届けることが出来ます。
変数名を指定すると、値をその変数に設定することが出来ますよね。
通常の変数に対して、配列変数は上記のようなアパートまたはマンションのような建物のイメージです。

「●県●市●番地●号」だけでは建物の住所だけですので、住所+部屋番号を指定する必要があります。
この部屋番号が配列で言う要素番号を指しており、「●県●市●番地●号−●号室」となります。
変数名と要素番号を同時に指定することで、指定した配列の指定要素に値を設定することが出来ますね。
HSPでは数値型や文字列型等全ての配列において、1〜4次元の配列変数を扱うことが可能で、
1次元配列では「配列変数名.要素」または「配列変数名(要素)」、
2次元配列では「配列変数名.要素.要素」または「配列変数名(要素, 要素)」、
3次元配列では「配列変数名.要素.要素.要素」または「配列変数名(要素, 要素, 要素)」、
4次元配列では「配列変数名.要素.要素.要素.要素」または「配列変数名(要素, 要素, 要素, 要素)」
という形式で記述することが出来ます。

配列変数は複数の変数が集まったものだと言うことだけを聞くと
「管理はしにくいままじゃないか」とそんな声が聞こえてきそうですね。
配列変数名は通常の変数名と同じ様にHSP規則に則って決めるわけですが、通常の変数と異なるところは、
この「要素」は添字番号で管理されており、各要素への参照や代入は数値を使用します。

 1
 2
 3
 4
 5
 6
	hairetsu.0 = 1 // 1つ目の要素に代入
	hairetsu.1 = 2 // 2つ目の要素に代入
	hairetsu.2 = 3 // 3つ目の要素に代入
	mes hairetsu.0 // 「hairetsu.0」は「hairetsu」と書いても良い
	mes hairetsu.1
	mes hairetsu.2
1行目のように1つ目の要素番号は0で、最大要素番号は「配列変数の最大要素数−1」となっています。 値の代入は下記のように一度にまとめてセットすることもできます。
 1
 2
 3
 4
	hairetsu = 1, 2, 3 // 一気に入れます
	mes hairetsu.0
	mes hairetsu.1
	mes hairetsu.2
コレのどこが普通の変数と異なるかと言うと、 勘の鋭い方は気づかれていると思いますが、要素番号は整数値ですから、 数値定数や数値を入れた変数を要素番号とすることが可能なのです。
 1
 2
 3
 4
 5
 6
 7
 8
	// 値のセット
	repeat 3
		hairetsu.cnt = cnt + 1 // 要素番号にカウンタを使用する
	loop
	// 値の表示
	repeat 3
		mes hairetsu.cnt
	loop
配列では全部の値(要素)に対してループを使用することで一度だけで行えるようになりますが、 変数でこのようなことは行えませんよね?
 1
 2
 3
//	A.1.1 = 123, 456, 789
	A.2.2 = 100
	A.0.1 = 1, 2, 3
1行目をコメントアウトしていますが、コメントを外すとエラーとなってしまいます。 1次元配列は一度に初期化できましたが、2次元以上では要素の自動拡張を完全には行ってくれません。 要素1.1は拡張してくれるものの、456789を入れる要素分までは用意してくれません。 3行目が一度に代入しているのにエラーとならないのは、既に要素が確保されているからなんです。 分かりにくいですが、どの要素に確保されるかと言うと、 「A.0.1=1,2,3」は「A.0.1=1」「A.1.1=2」「A.2.1=3」と同じになります。 2行目で要素2.2まで自動確保を行ってくれています。 もし、2行目が「A.1.1=100」ならエラーとなります、是非覚えておきましょう。 多次元配列はデバッグウィンドウで2次元目以降の要素確認が行えませんので、 何か問題が発生した時に非常にデバッグし辛いプログラムとなってしまいます。 便利な配列ですが、出来る限り、使用しても2次元までに抑えるようにしましょう。 変数の説明の時には配列の話が絡む為に行いませんでしたが、HSP2から変更点があります。 HSP2の変数は明示的に変数サイズを指定しなければ、サイズは64バイトとなっていました。 半角文字列であれば60文字強、全角文字列で30文字強。 数値を代入した場合は自動的に配列変数となり、要素16となっていました。 HSP3の変数は自動的にサイズが増加する仕組みになっており、 文字列型の初期サイズは0、数値の場合は要素が1つしかない変数となっています。 文字列はサイズが、数値も要素が自動的に増加する仕組みとなっている為、例えば数値であれば、 2番目(要素1)に入れると要素数2の配列変数になりますし、99番目に入れれば要素数100となります。 ただし、増加するのはあくまで値をセットする場合だけです。 代入していない要素・サイズ以上の位置の取得や表示を使用とするとエラーになります。 また、HSP3で変わった点が他にもあります。 HSP2では配列変数名と要素番号の区切りが「.」だけなので要素番号に式を入れることは出来ませんが、 HSP3から書ける様になった「()」を使うと、括弧の始めから終わりまでが1つの要素であるため、 計算式やほかの配列変数を要素番号にすることが出来るのです。 当然、指定される配列要素は「計算式の結果」です。 括弧で指定する場合の次元区切りはピリオド「.」ではなくコンマ「,」ですから間違えないようにしましょう。
 1
 2
 3
 4
 5
 6
 7
 8
	A.1.1.2 = 2
	X = 1
	buf(2+X) = 3
	buf(A.1.1.2) = -2
	buf(buf(2+X) + buf(A.1.1.2)) = 999
	mes buf(1)
	mes buf(2)
	mes buf(3)
HSP2では、要素指定の前に一旦変数に演算もしくは配列要素を代入して、 その変数を要素として使用する、といったワンステップ多い処理が必要でした。 この書き方ができるようになり、同じ処理でもスクリプトがシンプルになりますね。 さて、先ほどまで配列に整数のみを代入してきました。 コレは整数を入れた後に実数値だから代入していなかったわけではありません。 例えば初めから実数のみを配列にセット「v.2 = 3.65」と書くと、エラーが発生してしまいます。 値を混ぜていないのに異なる型といわれてしまいましたね。 変数は、数値の0が初期値として入っていると言うのを覚えてますか? 厳密には「整数値の0が入っている」=「整数型で初期化されている」わけです。 コレにより、整数型配列変数の要素2に実数を入れようとして型違いエラーと認識されたのです。
 1
 2
	v.0 = 0.0 // 変数vは実数型として再初期化
	v.2 = 3.65
別に「0.0」以外で構いませんが、要素0に実数値を代入することで初めて変数vは実数型となります。 実数型だけでなく文字列型も同様です。
 1
 2
	abc.0 = "" // コメントにするとエラーとなる
	abc.4 = "あいうえお"
整数以外を配列に入れる場合、必ず上記の様なコメント必須なことをしなければならないのかと言うと、 そういうわけではありません。 他言語では比較的当たり前のことなのですが、変数を使用する前に初期化する処理が別途用意されています。
ddim 変数名, 要素1(, 要素2, 要素3, 要素4)
変数名実数値を扱いたい変数名を指定する。
要素1配列1次元の要素数を指定する。
要素22次元目も必要な場合は指定する。(必要なければ直前のコンマ以降は書かない)
要素33次元目も必要な場合は指定する。(必要なければ直前のコンマ以降は書かない)
要素44次元目も必要な場合は指定する。(必要なければ直前のコンマ以降は書かない)
使用する変数を実数用の配列としたい場合は、HSP3で新しく登場した上記のddim命令を使用します。
 1
 2
 3
	ddim float, 10 // 要素数10の実数型配列を定義する
	float.3 = 3.14159
	mes float.3
一手間掛ける点は「0.0」方式と変わらず「以前書いた方法でもいいよ」となりますが、 多次元配列の場合は、うまく自動拡張されないのでこの方法で初期化しておく必要があります。 尚、このddim命令は、これから後に紹介する命令とは違ってマクロ命令の1つです。 使用する前には、「HSP拡張マクロを使用する」チェックを入れておきましょう。
 1
 2
	ddim f, 5, 2
	f(1, 1) = 1.234, 56.7, 8.90
文字列型の配列変数初期化命令も別途用意されています。
sdim 変数名, 文字数(, 要素1, 要素2, 要素3, 要素4)
変数名文字列を扱いたい変数名を指定する。
文字数1要素に代入する文字数を指定する。
要素1文字列型配列にする場合は指定する。(必要なければ直前のコンマ以降は書かない)
要素22次元目も必要な場合は指定します。(必要なければ直前のコンマ以降は書かない)
要素33次元目も必要な場合は指定します。(必要なければ直前のコンマ以降は書かない)
要素44次元目も必要な場合は指定します。(必要なければ直前のコンマ以降は書かない)
 1
 2
	sdim string, 12, 10
	string.5 = "Test"
「整数や実数と違い、文字列と言うのは初めから配列のようなもの」ですが、この意味は分かりますか? 例えば「"ABC"」は「'A'」「'B'」「'C'」がそれぞれの要素に入っているイメージです。 その為、ddim命令では1次元要素数としていた箇所がsdim命令で1次元目の文字数となったわけです。 文字列型の1次元配列にするには、例えば「sdim string, 12, 10」とすることで 約12バイト分のテキストを10個使うことが出来るようになります。 「『約』12バイト」と書いておりますが、代入できる文字にすると11バイトになる為です。 HSPを今回初めて利用する方には「何故?」とお思いのことと思います。 HSP3では自動拡張されるようになった為に、意識する必要がほとんどなくなりましたが、 文字列の末端には文字列の終了を意味するEOF(HSPではNULL)文字が自動的に付けられます。 HSP2以前は文字列サイズ+1を確保せず、ギリギリまで使用してしまうと、 溢れが発生してバグの原因となっていたものです。 HSP3なら自動拡張されるようになった為、直接バグにはなりませんが、 自動拡張処理が入るのと入らないのとでは処理速度に影響が生じますので、 出来る限り自動拡張せずともいけるようにプログラムをすべきでしょう。 まぁ今回の対応策として、具体的には少し大きめに確保すると言うことになると思いますが…。 また、溢れが生じる可能性がある(あった)と言うほかにも、 最後に付加されるNULLの存在を意識しないというのは文字列操作の時に問題を起こす可能性があります。 HSPのテキスト系命令は、間に制御文字であるNULL(=0)が存在すると、 後に文字列が続いていてもNULL文字までを表示対象としているからです。 この件については文字列操作系命令の紹介の際に再度注意書きをしますし、 初めのうちは、そこまで神経質になる必要はないかと思いますが、 とりあえず今は余談として覚えておいて損はないでしょう。
dim 変数名, 要素1(, 要素2, 要素3, 要素4)
変数名整数値を扱いたい変数名を指定する。
要素1配列1次元の要素数を指定する。
要素22次元目も必要な場合は指定する。(必要なければ直前のコンマ以降は書かない)
要素33次元目も必要な場合は指定する。(必要なければ直前のコンマ以降は書かない)
要素44次元目も必要な場合は指定する。(必要なければ直前のコンマ以降は書かない)
実数型、文字列型の配列定義命令があるように、整数型にも配列定義命令が存在します。 dim命令というもので書式と指定する各パラメータの意味はddim命令と同じです。 整数型の場合はdim命令を使わず、いきなり「buf.10 = 3, 2, 1」とやってもエラーになりませんので、 ddim命令、sdim命令に比べ、使用頻度は下がるかもしれません。 ただ、ddim命令、sdim命令にも言える事ですが、領域を確保すると言う意味でなく、 逆の「確保しすぎた領域サイズを縮める(=メモリ縮小)」という意味では存在意義があることでしょう。
 1
 2
 3
 4
 5
	temporary.100 = 1, 2, 3, 4, 5, 6, 7, 8, 9 // 一時的に配列サイズ108までが必要であったとする
	repeat 9, 100
		mes temporary.cnt
	loop
	dim temporary, 3 // 使用メモリ縮小
HSP2から使用している方の為に、余談をもう1つしておきましょう。 余談ですので読み飛ばしていただいて問題ありません。 HSP2の仕様では、「sdim命令は必ず4の倍数分のメモリが確保される」と言うものでした。 つまり、1〜4バイトどのサイズ分を確保しても、確保サイズは4バイト。 5バイトにすると8バイトになり、9バイトや10バイトでは12バイトになります。 これは、デバッグウィンドウを表示させてみるとお分かりいただけるかと思います。 仕様により、HSP3で4バイト単位しか確保できないと言う問題はなくなりましたが、 最低確保サイズが64バイトとなったようです。(sdim命令に限る) つまり、4バイトや32バイトの確保でも64バイト分確保されますが、 65バイトの指定であれば65バイト分のみ、と言った具合です。 sdim命令に限る、と言うのはdim命令だと64バイト以下の4バイトや8バイト分が可能と言うことです。 ddim命令でも同様で、64バイト以下の8バイトや16バイト分のみの確保が可能です。 その分、dim命令で3バイトや7バイト、ddim命令で4バイトや10バイトの確保はできません。 これはHSP2でも同じことでdim命令は4の倍数分、ddim命令は8の倍数分しか確保できなくなっています。 整数型は4バイト(32ビット)幅、実数型は8バイト(64ビット)幅を持たせているから、なんですけどね。 次にHSP3.2β3で追加されたラベル型の配列変数定義命令を紹介します。
ldim 変数名, ラベル数
変数名対象とする変数名を指定する。
ラベル数変数用に割り当てるラベル数を指定する。
第1パラメータはラベル型配列変数にしたい変数名を指定します。 ラベル型変数とは「ボタンやgoto命令等でジャンプするラベルを入れる変数」のことで、 HSP3から使用できるようになった新しい型。 第2パラメータは他の配列変数初期化命令と違い「幾つラベルを格納するか」最大ラベル数を指定します。 HSP3で備わった変数の自動拡張機能により、 確保済み以上のラベルを代入すると他の型の配列変数同様に自動で最大要素数が増加しますが、 再確保処理はマシンに対して余分に負荷を与えてしまうので、出来ることなら避けるべきですね。 尚、あまり意識する必要ないと思いますが、ラベル型変数1つ分で4バイト消費します。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
*label_0
	randomize
	ldim lbl, 3
	lbl = *label_1, *label_2, *label_3
	gosub lbl(rnd(3))
	mes "label_0 に戻りました"
	stop

*label_1
	mes "label_1 が実行されました"
	return

*label_2
	mes "label_2 が実行されました"
	return

*label_3
	mes "label_3 が実行されました"
	return
ラベル型配列変数定義を行うldim命令の紹介ついでに、 HSP3.2β3で追加されたラベル型変数を初期化するnewlab命令も説明しておきましょう。
newlab 変数名, ラベル
変数名初期化する変数名を指定する。
ラベル以下のいずれかを指定する。
ラベル名指定時は通常の変数代入と同じ効果。
「0」指定時はnewlabの次処理位置を記憶し、移動可能とする。
「1」指定時はnewlabの2つ先の処理位置を記憶し、移動可能とする。
サンプルが同梱されておらず、新しい機能であることから参考となる資料も見当たらない、 更に、使い方が特殊っぽいことからココで書く説明が的を得ているのかわかりませんが、 実際に試してみて「恐らくこうだろう」という内容を書いておきます。 第1パラメータにラベル型として初期化する変数を指定し、 第2パラメータに存在するラベル名か、数値で0または1を指定してください。 ラベル名を指定すると、通常の変数に代入するのと同じ結果となります。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
*label1
	mes "label1"
	gosub *label2
	gosub lbl           // 変数lblに入れた*label3へジャンプ
	stop

*label2
	mes "label2"
	newlab lbl, *label3 // 「lbl = *label3」としたのと同じ
	return

*label3
	mes "label3"
	return
数値0を指定した時は、newlab命令の次の処理位置を記憶し、 例えそこにラベルが存在しなくてもgoto命令やgosub命令等で移動できるようになります。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
*label1
	mes "*label1"
	gosub *label2
	gosub lbl     // 「mes "*label2"」の位置へジャンプ
	stop

*label2
	newlab lbl, 0 // 「mes "*label2"」の位置を変数に入れる
	mes "*label2"
	return
数値1を指定した時は、newlab命令の次の次の処理位置を記憶し、 例えそこにラベルが存在しなくてもgoto命令やgosub命令等で移動できるようになります。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
*label1
	mes "*label1"
	gosub *label2
	gosub lbl      // 「*label3」へジャンプ
	stop

*label2
	mes "*label2"
	newlab lbl, 1  // 「*label3」の位置を変数に入れる
	return

*label3
	mes "*label3"
	gosub *label2
	return
最後にdim命令やsdim命令が存在しなかった頃の変数サイズ拡大命令を紹介します。
alloc 変数名, サイズ
変数名対象とする変数名を指定する。
サイズ変数用に割り当てるサイズをバイト単位で指定する。
上に書いたようにallocdim命令やsdim命令が出現するまでに使われていた命令であり、 HSP3では標準命令から排除され、上位互換のために標準マクロ命令として残されているだけなので、 いつ消えてもおかしくはないものでしょうから使用は控えるのが無難かと思いますが、 存在する限りは入門講座用として、命令の紹介だけは一応しておきます。 alloc命令を使用すると、変数の型は文字列型に変わります。 数値の配列用という意味としても用いられたことでしょうが、実用したことはありません。
 1
 2
	alloc S, 128
	S = "128バイト分確保しておきましょう。その後の使い道はご自由に、ご利用は計画的に。"