プチコン3号入門講座

変数は変な数?


変数とはどんなものか



 プログラムを作る上で欠かせないのが変数です。

 そもそも変数とは何なのかというとそれを一時的に記憶しておく箱のような存在です。
 例えば、A=5とすれば「A」という箱の中に5という数字が入ります。分かりやすく言えば電卓のメモリー機能に相当します。
 ここで間違えやすいのが「Aと5が等しい」と考えてしまうことです。確かに数学ではそうなのですが、プチコン3号でプログラムを作る場合には「=」は代入と考えてください
 左に代入される変数、右に代入する値を記述します。例えば5=Aのように左右逆に記述した場合にはエラーとなります。

 変数にはアルファベットと数字と「 _ 」(アンダーバー)を組み合わせて好きな名前を付けることができます。ただしプチコン3号で使われる命令と同じ変数や数字で始まる変数は付けることができません。(アルファベット小文字は大文字と同じ扱いになるため変数Abcと変数ABCは同じ変数扱いとなる)

《 使用できる変数名 》
ABC=12
 OK
_ABC=23
 OK
1ABC=34
 NG (※数字で始まる変数名は使用できない)
PRINT=45
 NG (※命令語と同じ変数名は使用できない)
PRINT1=56
 OK (※PRINT1という命令はないため使用できる)
※変数名はスコアならばSCOREという名前にするなど自分で分かりやすいものにするのがベターです。
 あまり長い変数名にすると入力も大変だし、打ち間違いのリスクも増大するため50文字とか100文字とかの長い変数名は避けて適度な省略も必要になる場合があります。
 子音を並べていくとか頭文字を数文字取るとか省略のルールは作る人の自由なので後から見て自分が分かりそうなものにすると良いでしょう。
 《 省略例 》赤くて強い敵キャラの残り体力を示す変数名 RED_STRONG_ENEMY_REMAIN_HITPOINT → (省略後)RED_ENE_HP


 なお、多くのプログラミング言語では変数を使用する前には使えるように宣言する必要があります。プチコン3号ではVAR Aとすれば変数Aが使用できるようになりますが、プチコン3号では一般的なBASIC言語と同じく宣言をしなくても変数はプログラム上で使われる段階で自動的に使えるように設定(RUNで実行開始時に変数の識別が行われて、プログラムで該当の変数部分を実行することでメモリ上での変数確保)が行われるため宣言無しで変数を使用することが可能です。
 宣言無しで変数を使用することができるためDIRECTモードで変数を使った計算ができるだけではなくプログラム中を停止ボタンで停止した際に変数の内容を確認することも可能になり非常にお手軽な仕様となっています。(他のプログラミング言語経験者で宣言無しで使えることに不満を感じている人であっても下記のように設定によって宣言を必須にすることもできるため問題ない)

 変数の基本的な使い方について書いておきます。

 DIRECTモードでA=5 [ENTER] としてみてください。(以下DIRECTモードでの[ENTER]は書くのを省略するけど押されるまで実行されないことに注意して欲しい)
 PRINT A とすれば変数A5という数字が入っていることが確認できます。
 次に B=A+3 とした場合に変数Bの値はいくつになるでしょうか?

 変数Aの値が5なので変数Bには5+3=88という値が入っています。
 では、PRINT B で確認してみましょう。

 次に A=A+1 とした場合に変数Aの値はいくつになるでしょうか?
 これは5+1=6なので変数Aの値は6になるのがすぐに分かると思います。

 このように変数の値を1増やすという単純加算はインクリメントと呼ばれています。逆に1減らす単純減算はデクリメントと呼ばれています。(プチコン3号にはインクリメントは専用の命令INC、デクリメントは専用の命令DECが用意されていてINC AとすればA=A+1と同等の結果を得ることができるので長い変数名を使用している時に同じ変数名を2回タイプせずに済むため積極的に活用すると良い)
 変数SCOREにゲームのスコアが入っている時には、SCORE=SCORE+10でスコアが10点加算可能です。INC命令を使って10ずつ加算する場合はINC SCORE,10とすれば良い)

 これは余談ですが、プチコン3号でインクリメントを行った場合には(時間をかければ)無限大まで増え続けるということはなく9007199254740992が上限値になります。これが、プチコン3号で正しく表示可能な最大の整数値となります。なぜ、こうなるのかというとプチコン3号で表すことのできる数字(有効桁数)は下記のように16桁未満しかないためです。
 そのためDIRECTモードで整数部分が17桁以上の大きな数値を表示すると全桁を表示できずに末尾に0が羅列されてしまいます。また(絶対値が)0.000000005未満の小さな数値はDIRECTモードで表示すると0と表示されます。これはプチコン3号は普通に表示した際には小数第8位までしか表示されない(小数第9位を四捨五入して表示される)ためです。(プチコン3号は308桁くらいの数を扱うことができるけど有効桁数の関係で全桁正しく表示はできない)


数値変数と文字列変数



 プチコン3号では一般的なBASICと同じように数値変数と文字列変数が使用できます。
 数値変数というのは上記のように数字を記録する変数なのですが、文字列変数というのは文字列を記録できます。文字列というのはPETITCOMプチコンなどのように文字が連なったもののことです。
 文字列変数は変数名の最後に「$」を付けます。

 A$という文字列変数にABCという文字列を代入するとします。その際に A$=ABC とするとSyntax errorになります。文字列変数は代入する文字列は""(ダブルクォーテーション)で囲む必要があります。
 つまり、A$="ABC"で文字列変数A$ABCという文字列が代入されるというわけです。
 文字列も数値と同じく足し算が可能です。

 A$="123"
 B$="456"

 C$=A$+B$

 とした場合にC$にはどんな文字列が入っているでしょうか?
 123+456=579だからC$には579という文字列が代入される・・・というわけにはいきません。文字列の中に数字が含まれていてもあくまでそれは文字なので普通に足したりはできません。
 ではどうなるかというとA$+B$A$"123"という文字列のあとにB$"456"という文字列が結合されるだけです。つまり、C$=A$+B$の場合はC$には"123456"という文字列が入っているわけです。
 本当かどうかは、PRINT C$として確かめてみましょう。

 また、プチコン3号では文字列の掛け算ができます。A$="ABC"*3とすると"ABC"を3回足したのと同じ値になります。つまり、A$="ABC"*3"ABC"+"ABC"+"ABC"と同じ意味になりA$には"ABCABCABC"という値が入ります。同じ文字列がたくさん続く場合に使うと良いでしょう。

 文字列の長さ(文字数)はLEN()関数を使用すれば分かります。例えば、A$="ABCD"の時LEN(A$)の値は4になります。また、A$に何も入ってない時には0となります。
 文字列の中から特定の場所の文字を取り出すにはLEFT$()RIGHT$()MID$()を使えば可能です。左端から何文字分、右端から何文字分、任意の場所から何文字分という指定によって取り出すことが可能です。A$=MID$("ABCDEFG",3,2)とした場合は"ABCDEFG"の3番目から2文字分取り出すためA$には"CD"が入りそうですが、最初の1文字目は0番目であるため3番目の文字は4文字目となりA$には"DE"が入ります。(昔のBASICはMID$()の基準点は1だったけどプチコン3号は基準点が0となっているのは下記の配列変数と同じであり、これはC言語等の他の言語と同じであるため非常に分かりやすい)

 ちなみにA=123A$="ABC"においてAA$変数なのですが、123"ABC"定数といいます。そして、定数はリテラルとも呼びます。プチコン3号を使う限りは特に覚える必要はありませんが、123整数リテラル(小数点を含む数値ならば実数リテラル)、"ABC"文字列リテラルと呼びます。
 プチコン3号ではこれ以外の定数としてver.3.2.0から加わった定数リテラルがあります。例えば#AAボタンを押している時のBUTTON()関数の戻り値である16が入っています。プチコン3号上では、#A16は全く同じ扱いになっています。それは、プチコン3号は実行前に仮想マシンコードへと変換していてその際に#A16へと変換されるためです。

 また、プチコン3号では数値変数においては実数型と整数型の2つが使用可能です。
 整数型はその名の通り整数のみを使用可能な変数でA%のように変数名の最後に「%」を付けることで使用できます。-2147483648から+2147483647の範囲の整数を扱うことが可能で計算結果がこれを超えた場合はOverflowエラーとなります。この演算可能範囲はマイナス2の31乗からプラス2の31乗-1となっています。
 実数型は小数部を含む値を使用可能な変数でA#のように変数名の最後に「#」を付けることで使用できます。-1.79769313486231570e+308から1.79769313486231570e+308の範囲の数値を扱うことが可能です。これは、絶対値が2の1024乗未満の数を意味します。(309桁の数値を正確に表現できるというわけではなく有効桁数は16桁弱しかない)
 実は、プチコン3号の「実数型」というのは一般的なプログラミング言語では倍精度浮動小数点(C言語ではdoubleに相当)と呼ばれているものです。指数表記が行われていて小数点の位置が固定ではないため「浮動小数点」なのですが、「倍精度」というからには「倍ではない精度(単精度)」浮動小数点(C言語ではfloatに相当)もあります。しかし、プチコン3号には単精度浮動小数点型が無く倍精度しか使えないため「倍精度浮動小数点型」ではなく「実数型」と説明されていると思われます。

 実数型においては整数型とは異なり演算有効範囲を超えてもエラー表示が行われずinf(無限大)として扱われるため注意が必要です。一旦infになったら正しい計算はできないですが、infかどうかはver.3.1.0から加わったCLASSIFY()関数を使えば値を表示して目視しなくてもプログラム内で判別可能です。また、inf-infinf*0といった計算をすると解無しになるため非数であるnanを返します。これもCLASSIFY()関数を使えば判別可能です。

 ちなみに「%」「#」を付けてない変数Aはデフォルト(標準設定)では実数型になります。つまり、意図的に整数型を指定しない限りは数値変数を使用する場合にはすべて実数型として扱われているというわけです。
 整数型は初心者の間は使う機会はあまりないと思いますが、実数型の半分のメモリで済んだり、少しだけ演算速度が速いため必要と感じる場面で使うと良いでしょう。(最初にOPTION DEFINTを付けるとすべての数値変数をデフォルトで整数型になり「#」を付けない限り実数型にはならないため使用するのが整数がメインで少しでも速度を稼ぎたいプログラムを作る機会があれば活用すると良いかもしれない)
 あと注意すべき点はA#A%は異なる変数として処理されるという点です。使い分けが難しければ最初はデフォルトの実数型の数値変数のみを使用すると良いでしょう。(実数型は小数が扱えるというだけではなく整数型よりも大きな範囲となる-9007199254740992+9007199254740992の整数を扱えるというメリットもある)
 ただし、実数型には「実数型の誤差による誤動作を回避するための方法」に書いているような問題点もあるため注意が必要です。

 変数の内容を初期化したい場合、数値変数ならばA=0というように0を代入すれば良いのですが、文字列変数の場合はA$=""というように""の間には何も入れなければ初期化できます。ただし、これは初期値に戻すだけであり、メモリ上から解放されるわけではありません。
 変数をメモリ上から解放したい場合はDIRECTモードでCLEAR命令を実行してください。
 CLEAR命令はmkIIまでとは異なりDIRECTモード専用命令となっていてプログラム内では実行できません。(ただし、プログラム実行時に自動的に初期化されるため問題ない)
 なお、CLEAR命令ではすべての「グローバル変数」の初期化(解放)が行われますが、「ローカル変数」の初期化(解放)は行われません。ローカル変数の初期化はDEFを終了時(自作関数内のEND実行時)かプログラム実行時(RUNもしくはEXEC)に自動的に行われます。ちなみにグローバル変数はプログラムスロットによって異なります。要するにプログラムスロット1の変数ABCはプログラムスロット0の変数ABCとは別の物になるというわけです。プログラムスロット1のグローバル変数のみを初期化したい場合はUSE 1としてください。

 ローカル変数はDEF命令によって作られる自作関数の中だけで使用可能な変数であり、DEF外で使用する普通の変数(グローバル変数)とは同じ変数名であっても別物として処理が行われます。そのためグローバル変数と変数名の被りを心配することなく使用することができます。(自作関数を実行時に確保され終了時に自動的に解放されるため使い捨ての変数となる)
 後述の「DEFで自作関数を作ろう」で詳しく書きますが、自作関数内でローカル変数を使用するにはVARで変数の宣言をする必要があります。自作関数内でVARで宣言しない場合はその自作関数より前に同名のグローバル変数が記述してあればグローバル変数扱いになり、無い場合はローカル変数扱いになります。
 したがって、自作関数内でグローバル変数を使用する場合はプログラム内の変数の記述場所にも注意が必要です。(自作関数をプログラムの先頭に記述している場合はグローバル変数として認識されず、ローカル変数として動作してしまうため別途先頭でグローバル変数の宣言が必要になる)
 
 数値を文字列に変換したり、文字列を数値に変換する方法については「数値にする?それと文字にする?」で書いているのでそれを参考にしてみてください。
 また「文字列と数値を変数を分けることなく文字でも数値でも入る万能変数があればいいのに・・・」と考える人もいるかもしれません。それは本来はプチコン3号にはそんな機能は存在しないのですが、私の自作関数INIT()を使えば可能です。INIT()関数はこちらです。なぜ、そんなことが可能なのかは、プチコン3号で配列を扱う場合の注意点に詳しく書いています。
 このINIT()関数を経由して文字列を代入すれば自動的に文字列変数扱いになり、数値を代入すれば自動的に数値変数扱いになります。また、代入する数値も実数値(小数点を含む値)ならば自動的に実数型になり、整数値ならば自動的に整数型になるという優れものです。


ひとまとまりのデータを扱うならば配列変数を使う



 数値変数や文字列変数が箱ならば配列変数はロッカーに相当します。つまり、順番に並んだ入れ物ということです。ロッカーが何番のロッカーに何が入っているのかというのがすぐに分かるように配列変数は番号指定を自由に行うことができその番号の変数を自由に活用できます。

 DIM A[3] DIMを使って宣言することでA[0]〜A[2]の3つの配列変数が使用可能(※配列変数は宣言無しで使用することはできない
 番号は0番から始まっているためDIM A[3]で3つの配列変数を確保しても3つ目はA[2]になる
 A[0]=10 要素0の配列変数A[0]に10を代入
 A[1]=15 要素1の配列変数A[1]に15を代入
 A[2]=20 要素2の配列変数A[2]に20を代入
 A[3]=25 要素3の配列変数A[3]に25を代入
 A[3]は宣言されている配列変数の範囲外なのでSubscript out of rangeエラーとなる

 これならば「A0=10:A1=15:A2=20とすれば良いだけではないのか?」と思うかもしれません。配列変数ではない通常の変数ならば宣言をしなくても使うことができるためお手軽です。(通常の変数も VAR AGE,NAME$のようにして変数AGE、変数NAME$を使用することが宣言することが可能ですが、下記OPTION STRICTを記述しない限りは宣言無しで使用が可能。ただし、後述のようにDEF命令を使い自作関数を作る場合においてローカル変数を使う場合は宣言が必要になる。)※ローカル変数についての説明はここでは省略
 配列変数にどういうメリットがあるかというと例えば上位10位のスコアを入れておきたいという場合にS1S2S3、・・・、S10のように10個の変数を用意するのは大変です。10個ならまだマシで100個、1000個になるとその都度全部代入処理を記述していくとなるとプログラムが無駄に長くなります。配列変数ならばS[0]S[9]の10個の変数を確保しておけば解決です。
 後述FOR〜NEXT命令と組み合わせると下記のように記述するだけで10個の変数に100という値が入ります。

FOR I=0 TO 9
  S[I]=100
NEXT

 これが100個の変数になってもTOの後の繰り替えし数が増えるたけなので簡単に対応できます。配列変数はこのように括弧内の数字(「添字(そえじ)」という)に変数を使うことができるというのが最大のメリットになります。つまり、たくさんの変数を使うけど変数の番号を変数で指定することが無いのならば配列変数を使う必要はないということです。(ただし、プチコン3号の場合は数値変数の値をセーブする場合は配列変数を使う必要があるためセーブデータに関しては例外)
 ちなみにver.3.2.0からFILL命令が加わり上記のように配列変数S[]のすべてに100という値を書き込みたい場合はFILL S,100とすることができるようになり、同じ値を入れたいという場合にループの必要性もなくなりました。

 例えば、トランプの52枚のカードを変数に入れてそれを自由に表示したいという場合は52個の配列変数を確保すれば簡単に処理が行えます。

《 トランプ表示 サンプルプログラム 》
ACLS 画面の消去(ACLSは表示関係をすべて初期化できる)
BACKCOLOR #GREEN 背景色を緑にする
VAR C[52],I,S=500 変数の宣言(配列S[]を52個確保、Sの初期値は500
FOR I=0 TO 51 ループを0から始めて51まで(52回分)繰り返す
 C[I]=I 配列C[]にカード番号を0から順に割り振る
NEXT ループの終了
FOR I=0 TO S ループ1から始めてSの値まで(S回分)繰り返す
 SWAP C[RND(52)],C[RND(52)] 52枚のカードからランダムな2枚を交換することでシャッフルを行う
NEXT ループの終了
FOR I=0 TO 51 ループを0から始めて51まで(52回分)繰り返す
 CARD C[I] カード番号Iのカードを表示(表示は後に定義しているCARD命令で行う)
NEXT ループの終了
END プログラムの終了
 
DEF CARD C カードを表示するCARD命令を定義
 VAR A,B,C$ 変数を宣言(これら3つの変数はDEFEND内での宣言であるためローカル変数となる)
 A=C MOD 13 変数Aに「C13で割った余り」が入る(これがカードの数字の部分の元となる)
 B=C DIV 13 変数Bに「C13で割った商(整数値)」が入る(これがカードのマークの部分の元となる)
 IF A==0 THEN 変数Aの値が0ならば
   C$="A"   変数C$"A"を代入(カードの数字の部分が0ならば「A(エース)」となる)
  ELSEIF A<10 THEN 変数Aの値が10より小さいならば
 (ELSEIFと記述することですでに判定しているAの値が0の場合を除外できる)
   C$=STR$(A+1)    変数C$A+1を文字列化したものを代入
 (AではなくA+1としているのはカードの数字が0が「A」であるため数字が1のカードが「2」になるから)
  ELSE C$=MID$("JQK",A-10,1) それ以外の時は変数C$に"J"、"Q"、"K"のいずれかを代入
 (MID$においてA-10としているのは
  カードの数字が10の時に"JQK"の最初(起点となる0番目)の文字である"J"を選択
  カードの数字が11の時に"JQK"の1番目の文字である"Q"を選択
  カードの数字が12の時に"JQK"の2番目の文字である"K"を選択のため)
 ENDIF IF命令の終了
 C$=MID$("♠♥♣♦",B,1)+C$ 変数C$にトランプマークを追加する
 (MID$によってB(トランプマーク)が0の時はスペード、1の時はハート、2の時はクラブ、3の時はダイヤが選択される)
 COLOR (B MOD 2)*2+1 表示色を(B MOD 2)*2+1に設定
 ((B MOD 2)*2+1の値はB0の時は1B1の時は3B2の時は1B3の時は3となり
 スペードのカードは黒、ハートのカードは赤、クラブのカードは黒、ダイヤのカードは赤となる)
 PRINT C$, 変数C$(カード)を表示(最後に「,」が付いているのはカード同士がくっつかないようにするため)
END 自作命令CARDの定義を終了

 ※IFFORNEXTDEF等については後述参照

 このように配列変数を使えばカードを自由に表示可能になります。またSWAP命令で任意の2枚のカードを取り出し交換することでシャッフルを行うということも容易に可能になります。トランプゲームを作る場合にも配列変数を使えば簡単に判定が可能です。
 RPGを作る場合にもプレイヤーキャラや敵キャラが何体になっても添え字の部分を変更するだけで処理が可能になるため配列変数はあらゆるゲーム、ツールにおいて有効活用が可能であり、基礎知識として抑えておく必要があるでしょう。  ただし、何でもかんでも配列変数を使えばいいというわけでもなく簡単に記述できるから配列変数を使うわけであって例えばリンゴの数が2、ミカンの数が3という場合にDIM APPLE[1],ORANGE[1]:APPLE[0]=2:ORANGE[0]=3のように配列を使用するメリットはなく添え字に変数を使わない場合はDIM APPLE=2,ORANGE=3で問題ありません。

 配列変数は決まったひとまとまりのデータを扱うには非常に便利な変数ですが、上記のように使用する前には宣言が必要になります。DIM S[10]とすれば配列変数S[0]S[9]の10個確保されます。(1からではなく0から始まっていることに注意)
 配列変数でも文字列を扱うことが可能でその場合はA$[10]のように変数名の後に「$」を付けます。なお、トランプ表示サンプルで使用されていますが、DIMではなく普通の変数のようにVARで配列変数を宣言することは可能です。変数を宣言する場合において、どちらも全く同じ動作になるため好きな方を使うと良いでしょう。DIMは一般的なBASICでの使い方なのでプチコン3号で新規にプログラミングを覚えるならば配列変数のみにDIMを使う必要はなくVARで統一して全く問題ない。他の人が作ったプログラムを読む際に知らないと困るので(初心者は)DIMとVARは同じもの」と覚えておけばOK)

 あとプチコン3号では配列変数では要素の追加や削除も簡単にできます。
 末尾にデータを追加するにはPUSH命令を使用します。すでに配列変数A[10]まで確保A[0]からの11個の要素を確保)されている場合はPUSH A,100とすればA[11]が新たに確保されてA[11]には100という値が入ります。
 配列の末尾のデータを取り出すにはPOP()関数を使用します。B=POP(A)とすれば例えば配列変数A[12]まで確保されていてA[12]の値が100であれば変数B100という値が入りA[12]は削除されます。
 また配列の先頭に新しく要素を追加する場合はUNSHIFT命令を使い最初の要素を取り出しそれを削除するにはSHIFT()関数を使います。
 PUSHPOP()SHIFT()UNSHIFTを使って要素数を動的に変化させる場合には最初にDIM A[0]のように要素数は0を宣言しておくという使い方をする場合もあります。なお、現在確保されている要素数を知るにはLEN()関数を使うと良いです。A=LEN(B)とすれば配列変数B[]の要素数が変数Aに入ります。

《 PUSHを使ったサンプルプログラム 》
VAR A,I,K=64,X[K],Y[K]
FOR I=0 TO K-1
 SPSET I,345
NEXT
SPCOLOR K-1,#BLUE

@LOOP
FOR I=0 TO K-1
 SPOFS I,X[I],Y[I]
NEXT
STICK OUT SX,SY
PUSH X,X[K-1]+SY*4
PUSH Y,Y[K-1]-SY*4
A=SHIFT(X)
A=SHIFT(Y)
VSYNC
GOTO @LOOP
 FORNEXTSTICKVSYNCGOTO、スプライト関係に関しては後述参照

 これは先頭の青色のボールをスライドパッドで動かしその後を多数のピンクのボールが追いかけるというグラディウスのオプション的な動きを再現したものですが、数が多く間隔が狭いためミミズのような動きに見えるでしょう。これはKの値(初期値は64)のスプライトをすべて座標を配列変数に入れておいて順番に表示しているだけですが、PUSHを使い最新のデータを配列の末尾に追加してSHIFTで最初のデータの取り出し(削除)を行っています。取り出したデータは特に使う必要が無く破棄して良いため変数Aはダミーとなっています。  このプログラムを見て分かるようにPUSHPOP()UNSHIFTSHIFT()LEN()などで配列全体を指定する際には添え字部分は不要となります。したがって、A=SHIFT(X[K-1])ではなくA=SHIFT(X)と記述します。
 実はこのプログラムはPOPを使わなくてもMODを使い「リングバッファ」として記述することも可能なのですが、POPを使うことでリングバッファ無しでも記述が可能になるというサンプルとして考えてください。

 PUSHPOP()などはスタック処理を行うのには非常に便利ですが、初心者の場合だと何のためにあるのかが分かりにくい命令(関数)かもしれません。スタックというのはデータの出し入れを行うもので、例えば本を積み重ねていく場合に基本的に下から順番に積むためそこから取り出す場合には後から積んだ一番上の本から順番に取り出すことになります。このように積んだデータを取り出すということはデータ処理をする場合にはよくあることです。

 ここで注意しなくてはならないのは変数Aと配列変数A[]は同じプログラム内では使用できないということです。これは変数名を被って使うことがないようにするための措置です。(上記のように関数でA[]という配列全体を引数に使用する場合には上記のPOP命令と同じくA[]ではなくAで指定するため)
 その代わり、配列変数ではない文字列変数で配列変数のような操作(配列ではない文字列変数もプチコン3号の内部では配列を使って処理しているため)も可能になっています。A$="ABC"の時はA$[0]A$の1番左(つまり、"A"A$[1]A$の2番目(つまり、"B"A$[2]A$の3番目の文字(つまり、"C"が取得可能です。これはプチコン3号でのプログラミングに慣れないうちに使うと配列変数と混同してしまうだけではなくプチコン3号の配列は参照型となっていてその影響は配列ではない文字列変数にも現れるため配列変数ではない文字列変数を配列的に使わないことを推奨します(プチコン3号の配列に使われている参照型は下記で解説していますが参照型が分からなくてもこのように文字列を配列的に使用する場合は読み出し専用で使用すれば問題ない)。しかし、ある程度慣れてきたら積極的に使うことでプログラムをより短く、より速く動作させることが可能になるためこういう使い方もあるというくらいは覚えておいても良いかもしれません。(この文字列変数の配列的な使用方法は「プチコン3号公式ガイドブック」にも記載されている正式な仕様なので安心して使用してOK)
 このような配列変数的な使用方法をする場合には配列変数と同様にPUSHPOPなども使用することが可能です。(文字列の最後に1文字追加したり最後の1文字を削除したりできる)
 また文字列配列でこのような配列変数的な使用をする場合には添え字を2つ書きます。例えば配列変数A$[1]において0を起点とした4番目の文字(実質5番目の文字)を指定する場合はA$[1][4]とします。

 また文字列変数の配列的な使用方法は定数である文字列(文字列リテラル)においても有効です。例えば、A$="ABCDE"[N]で文字列"ABCDE"のN+1番目にある文字を取り出します。例えばN=2の時は3番目である"C"が変数A$に入ります。これはMID$で取り出す文字数を1文字で指定した場合であるMID$("ABCDE",N,1)と同じ動作です。
 文字列リテラルに添え字を付けて任意の1文字を取り出す方法は配列変数と混同することもないし、参照型になるということも考える必要もないため初心者であっても積極的に使っても良いかもしれません。MID$を使用するよりも簡単だし)

 配列変数は非常に活用範囲が広くてゲームやツールを作る際に必要になってくることも多いのですが、何度も書いているようにプチコン3号の配列変数は昔のBASICとは異なり参照型となっているという点だけは注意をする必要があります。
 といっても、これは配列全体を代入処理したり、文字列変数を配列変数のように使う場合に注意すべきというだけであってそれ以外の使用方法であれば「参照型」というのを意識せずに使用して問題ありません。
 なお、プチコン3号で配列変数を使う場合の注意点については下記に詳しく書いています。


変数をすべて宣言して使う



 実は、宣言が必要なのは配列変数だけではありません。配列変数ではない普通の数値変数や文字列変数であってもOPTION STRICTというのを最初に記述すると宣言が必要になってきます。(最初の行に記述せず途中の行に記述した際には記述した以降が有効になる)
 このOPTION STRICTをプログラム内に記述した場合、たとえば変数SCOREを使用するためには最初にVAR SCOREのようにVARを付けて使用するための宣言をしなくてはなりません。OPTION STRICTは変数の宣言を強制化するだけなのでOPTION STRICT無しで変数をVARによる宣言を行って使用することは何ら問題はありません)


OPTION STRICT
VAR SCORE=0


※変数SOCREは使用可能になり0という初期値が代入される
OPTION STRICT
SCORE=0


※変数SOCREは正しく宣言されてないためUndefined variableというエラーが出る

 初期値を設定したい場合はVAR A=5のように初期値を入れて宣言をすることができますが、初期値を特に設定せず、変数の宣言だけをしたい場合(変数Aを使える状態にしたい場合)VAR AだけでOKです。

 標準設定だと宣言をしなくても使える変数をわざわざ宣言して使うというのも面倒臭いように気もしますがタイプミスなどでSCOREという変数をSCOERと記述した際に定義されてない変数としてエラーを出すことが可能になるというメリットもあります。OPTION STRICTを付けなければSCOERという変数はそのまま使えるけどそれはSCOREとは別の変数であり見つけにくいバグを産んでしまう)
 OPTION STRICTを付けるかつけないかはメリット、デメリットがあるので作ろうとするプログラムの規模や厳格性を元に考えると良いでしょう。(変数名の打ち間違いが絶対にないという自信のある人以外はOPTION STRICTを付けるのがベター)


VARをバリバリ使いこなそう



 上記の配列変数において(初心者は)DIMとVARは同じもの」と覚えておけばOKと書いたのですが、実は異なる部分があります。
 ここではそれについて簡単に説明しておきます。(初心者の人はこの項目を読み飛ばしてもOK)

 DIMにはないVARの特長としては変数名を文字列で指定できるという点です。これはどういう意味かというとA=VAR("B")A=Bと同じことが可能になります。ただし、VAR()の中に文字列で指定された変数名はプログラムリスト内で使用されているものに限ります。VARで宣言されていなくても実際には実行されない場所に記述されていてもCHKVAR()で取得可能な状態であればOK)
 もちろん、VARの引数は文字列変数でも良いためA=VAR(B$)という記述も可能です。これはどういう動作をするかというとB$="B"の時はA=Bと同じ動作、B$="C"の時はA=Cと同じ動作になります。つまり、参照する変数を動的に変化させることが可能になるわけです。

 この動的変動は文字列化された式の評価を行う場合に役に立ちます。例えば"A+B"という文字列があり、これが変数Aと変数Bの値を足したものという意味ならばC=A+Bと記述すれば解決できそうですが、ABの部分は変化する場合にはC=D+EC=E+Fなどすべての組み合わせを記述する必要があり現実的ではありません。しかし、C=VAR(A$)+VAR(B$)とすればどのような組み合わせにも対応ができます。の演算子の部分も動的に変化するならばちゃんとそれが可能になるように記述する必要があるけどVARの説明とは大きくそれるため省略)


 さて、詳しくは後述の「DEFで自作関数を作ろう」で解説しますが、プチコン3号ではすべての場所で参照が可能なグローバル変数とその関数内でのみ参照可能なローカル変数があります。自作関数外で使用されている変数は宣言の有無に関わらずすべてグローバル変数自作関数内においてはVARやDIMを使って宣言されているものはローカル変数となっています。自作関数内で宣言されてない変数はプログラムリストにおいてその変数名の変数が最初に記述されている場所が自作関数内であればローカル変数、自作関数外であればグローバル変数となります。
 本来ならばすべての場所で参照可能なはずのグローバル変数ですが、プチコン3号においてはプログラムスロットごとにグローバル変数は異なる存在となっているためVAR("A")では現在動作しているプログラムスロットの変数Aの値が取得できるだけあり、他スロットに存在する変数Aの値を取得することはできません。(C言語等の他のプログラミング言語の経験者ならばスコープと考えれば分かりやすいと思う)
 しかし、VARの引数でプログラムスロットを指定すればその指定スロットのグローバル変数の値が取得可能になります。例えばA=VAR("1:Z")とすればプログラムスロット1のグローバル変数Zの値が取得可能になるということです。存在しない変数を参照しようとするとUndefined variableというエラーが発生してしまうため事前にCHKVAR("1:Z")のようにして指定スロットにその変数がちゃんと存在しているかどうかを事前にチェックしておくと良いでしょう。

 ここで注意しなくてはならないのは、その変数がプログラムに記述してあれば存在していると判断されるわけではないということです。ちゃんと記述されていても正しく取り扱わないとCHKVAR()falseと判定されてしまいその変数が存在してないことにされてしまいます。
 VARで他のプログラムスロットのグローバル変数を参照するには事前の準備が必要になります。それはプチコン3号ではRUNをするごとに仮想マシンコードに変換される(プログラムが実行可能な状態)ようになっていてその際にVARDIMで宣言していなくてもグローバル変数が使用可能になります。しかし、他のプログラムスロットのグローバル変数ではその処理が行われていません。したがって、仮想マシンコードへの変換をまず行う必要があるわけです。

 まず1つがUSEを使う方法です。USEを実行することで仮想マシンコードへの変換(プチコン3号は速度やメモリ重視のため1パスコンパイルとなっている)のみを行いプログラムが実行可能な状態になります。しかし、これではまだグローバル変数の値を取得可能な状態にはなりません(グローバル変数が実体化していない状態)USE 1としてスロット1が使える状態にしてスロット1のプログラムの最初でVAR Z=5と記述していてもVAR("1:Z")はエラーにならない0という初期値を返す)だけとなります。要するに変数名は取得できるけどその変数のための領域は確保されてない状態となっているわけです。
 USEは仮想マシンコードへの変換を行うためこの時点で文法エラー等の記述ミスがあればエラーを返しますが、この時点ではプログラムはまだ実行されてない状態です。
 ちゃんと他のスロットのグローバル変数の値を取得できるようにするためにはプログラムスロット1のラベル名@Vの行でグローバル変数の宣言やグローバル変数の使用が行われているならばGOSUB"1:@V"とすることでグローバル変数の実体化が行われます。プログラムスロット1に@V Z=7:RETURNと記述してあればVAR("1:Z")とすることで7という値を取得できます。

 また、EXECでも引数にプログラムスロットを指定することでも同じようなことが可能です。EXECUSEと同じく仮想マシンコードへの変換を行いますが、それだけではなく実行まで行ってくれる便利な命令です。(つまり、EXECを使用するならばUSEは不要)
 プログラムの先頭行にZ=9:ENDと記述していればEXEC 1の後にVAR("1:Z")とすることで9という値を取得できます。
 つまり、他スロットのグローバル変数の値を取得するには一旦その変数に代入処理まで行う必要があるということです。USE命令は仮想マシンコードの変換のみ行いたい場合やEXECでは最初の行から実行してしまうのでそれでは困る場合にGOSUBと併用を前提で使用すると良いでしょう。(特に問題はない場合は基本的にEXECを使用するのが最も簡単)
 配列変数を使う場合は代入部分ではなく変数の宣言部分を実行しておかないと使用することができないため注意が必要です。(変数名が取得できてその変数の存在自体が確認できても変数の宣言が必須であるため)

 これを利用すればプログラムスロットを跨いだプログラムを作ることが可能になります。ただし、VARによる変数の参照は必ずしもグローバル変数の値を参照ができるというわけではなくプログラムスロットを指定しない場合において自作関数内で参照する場合にはグローバル変数と同じ名称のローカル変数を使用していた場合にはローカル変数の値を参照するため注意が必要です。(逆にいえば普通は参照ができないローカル変数と同名のグローバル変数もVARでスロットを指定すれば可能になるということ)
 そのためには現在動作しているスロットを取得する必要がありますが、プチコン3号ではそれを取得する関数が用意されてないため自前で判断する必要があります。1つの方法としてはBACKTRACE命令を実行すれば画面上に現在動作しているスロット番号が表示されるためその文字をCHKCHR()で取得するという方法があります。

 上記の例ではいずれもA=VAR("B")のようなVARが右辺にくる場合のみを書きました。しかし、VAR("文字列")という記述を行う場合には左辺にVARを記述することが可能になります。プチコン3号だけに限らず一般的なプログラミング言語においては右辺には計算する式を記述してそれを左辺に記述した変数に代入するという記述方法を採っていますが、VAR()関数は計算対象だけではなく代入対象にもなるということです。右辺に記述の式を右辺値、左辺に記述の変数を左辺値と呼びます。
 例えばVAR("A")=BとすればA=Bと同じ意味になり、VAR(A$)=VAR(B$)とすれば任意の変数=任意の変数という意味になります。B$に入っている変数を参照した値が、A$に入っている変数に代入される)

 このVARが左辺値になるということを利用すれば現在動作しているプログラムスロットを取得することが可能になります。

《 動作しているスロットを取得するSLOT()関数 》
DEF SLOT()
VAR A$,B,I
FOR I=0 TO 3
 A$=STR$(I)+":_SLOT"
 B=CHKVAR(A$)
 IF B THEN VAR(A$)=1
 _SLOT=0
 IF B && !VAR(A$) THEN BREAK
NEXT
RETURN I
END
公開キー【 AEE3E8NV 】、ファイル名「SLOT」
※公開キーは予告無く削除や変更される場合があります。

 この関数を使用する場合はグローバル変数 _SLOT をあらかじめ用意しておく必要があります。
 簡単に動作原理を説明しておくと指定したスロット番号のグローバル変数_SLOTに初期値1を入れてその後に_SLOTの値を0にし指定したスロット番号の_SLOT0になっていればその指定したスロット番号が現在動作しているスロットとなるわけです。これは、動作しているスロットでは_SLOTの値は1から0に変わるけどそれ以外は変わらないためです。VAR(A$)=1という代入処理がないとたまたま他スロットに_SLOTというグローバル変数があり、さらに実体化した状態になっていてたまたま条件を満たしている値だった場合に誤判定をしてしまう)
 このSLOT()関数は、たまたま他スロットにグローバル変数_SLOTが存在してそれがすでに実体化した状態になっていてさらにそれが初期値0だったとしても実体化している時点で1が代入されるためこのプログラムではどのような条件下においてもSLOT()を実行するだけで動作しているスロットが正しく取得可能となっています。BACKTRACEを使った方法だと画面表示が必須になるため邪魔にならない位置に表示したりとか表示が見える前に消去したり画面に見えないように透明色にしたりという配慮が必要になりますが、このSLOT()関数ではそれは不要です。
 これを使えばスロットを跨いだプログラムを作れるだけではなくローカル変数と名称が被っているグローバル変数の値も取得できるようになります。

《 グローバル変数取得サンプルプログラム 》
VAR A=2
VARA

DEF VARA
VAR A=1
?A,VAR("A"),VAR("0:A"),VAR(STR$(SLOT())+":A")
END

 このプログラムを実行すれば自作関数内でVARを使いVAR("A")のようにスロットを指定しない場合はローカル変数の値を取得してしまうことが分かります。またVAR("0:A")のように「スロット0」を直接指定してしまうとスロット0で動作時は問題ないですが、他のスロットでこのプログラムを実行すると正しく動作しないということも分かります。
 それに比べてSLOT()関数を使ったVAR(STR$(SLOT())+":A")という記述であればこのプログラムをどのプログラムスロットに置いても問題なく動作するようになります。

 また、自作関数内では変数だけではなくGOTORESTOREなども基本的にはローカルラベル(自作関数内のラベル)を参照しますが、このSLOT()関数を使えば関数外部のグローバルラベルを参照することが可能になります。

 このようにプチコン3号のVARは変数の宣言をするだけならばDIMと全く同じとはいえ、機能面ではDIMとは大きく異なる部分があることが分かると思います。


プチコン3号で配列を扱う場合の注意点



 プチコン3号では次のようにして配列全体を別の配列に代入することができます。

VAR A[1000],B[0]
FOR I=0 TO 999
 A[I]=RNDF()
NEXT
B=A

 この例では配列変数A[]に0〜1の値が入った乱数を1000回分のデータが入っていますが、B=Aとすることで配列変数B[]に配列変数A[]がそのまま入ります。そのままの状態で入るため配列の要素数が異なる場合であっても代入される側は代入する側に要素数は合わせられます。この場合は配列変数B[]の要素数はA[]と同じく1000になるわけです。

 ここで注意する部分がプチコン3号において配列変数は参照型になっているということです。「参照型」というのはそのままの値が入る「値型」とは異なり実データは別の部分にあります。そのため配列を丸ごと別の配列に代入する際に1000の配列が2つ分の領域が確保されるわけではなく確保されるのはあくまで1つ分です。
 上記の例の場合だとA[]にもB[]には実データは入っておらずメモリ上にある別データを参照するだけとなります。したがって、A[0]=1のように別の値を代入したりPUSH A,1のように要素数を変化させるとそのデータを参照しているB[]にも同じことが反映されます。
 もちろん代入する側のA[]も参照型であるためB[]の要素数を変えたりするとA[]にも反映されます。

 配列ではない文字列もプチコン3号が内部では配列として処理している関係上A$="ABC"の時にB$=A$とした場合にはB$"ABC"という文字列が入りますが、これも参照型となります。つまり、メモリ上にある"ABC"というデータをA$B$から参照しているだけということです。
 そのためA$[0]="D"としてA$の最初の文字を"D"にすると実データが書き換えられるためA$="DBC"になると同時にB$="DBC"となります。
 配列ではない文字列を配列のように添え字を付けて扱う場合には配列と同等と考え参照型になっている点に注意が必要となります。
 またA$="ABC":B$=A$:A$=C$のようにA$に別の文字列を代入した場合にはその時点でA$B$は参照しているデータが変わるためA$の値を変えてもB$には影響を与えなくなります。A$B$C$がすべて同じ値を参照していると考えてしまわないように注意しましょう。

 このようにプチコンで扱う文字列は一般的なBASICと比べるとかなり特殊なのですが、配列的に使用する場合や文字列でINCDECを使用する場合を除き文字列変数を使用する際に参照型かどうかを意識する必要は全くありません。


 さて、配列を使って困ることは多くの配列を確保していくとどんどんメモリが圧迫されていくということです。いくらプチコン3号で使えるメモリが80年代のパソコンと比べて桁違いに多いといっても有限であり、大きな配列を確保すれば簡単にメモリが足らなくなります。  しかし、プチコン3号はCLEARがDIRECTモード専用命令になりましたし、一般的なBASICで多く用いられている配列変数を初期化するERASE命令等がありません。
 この場合はどうしたら良いかというとPOP()関数を使い要素を1つずつ削除していくという方法があります。要素数が0になるまでループを行うだけで簡単に初期化できます。(配列そのものの宣言を無かったことにするERASE命令とは異なりこの場合の初期化とは配列の要素数を0にして空きメモリを確保することを示す)

《 POP()を使用して配列変数A[]を初期化するプログラム 》
WHILE LEN(A)
 Z=POP(A)
WEND

 ただし、プチコン3号の配列の要素数が動的に変化できるのは1次元配列の場合のみで多次元配列の初期化は行うことができません。
 また、配列変数A[]だけではなく自由な変数名の配列を初期化したいという場合はVARを使ったり、DEFで自作命令(自作関数)を作ると良いでしょう。VARを変数の宣言以外に使用する方法については上記参照、DEFについて詳しくは後述の「DEFを使って自作関数を作ろう」を参照)

《 配列変数を初期化するプログラム 》
WHILE LEN(VAR(A$))
 Z=POP(VAR(A$))
WEND
 ※A$にあらかじめ配列変数の変数名を入れておく

《 配列変数を初期化するERASER命令 》
DEF ERASER V
 VAR Z
 WHILE LEN(V)
  Z=POP(V)
 WEND
END
 ※この自作命令はあくまで配列の初期化のみであり、配列そのものを消去する命令ではありません

 この2つの方法では変数名は自由に設定できるとはいえ数値配列のみに対応していて文字列配列には対応していません。どんな型でも初期化が可能にするためには数値型か文字列型かの型判別を行いどの場合でも正しくするように記述する必要があるのです。
 ただし、型判別を行う関数等はプチコン3号には用意されていません。したがって、自前でそのような関数を作る必要があります。これは、プチコン3号では現時点では数値と文字列の比較演算は3を返す仕様になっているというのを使えば簡単に作ることが可能です。(詳しくは後述の論理式で深まる条件判断を参照)

《 型を判別するSUFFIX()関数 》
DEF SUFFIX(V)
 VAR A
 A=V>0<3
 IF A THEN V=0.5:A=V*2+1
 RETURN A
END
※この関数では整数型は1、実数型は2、文字列型は0を返す
 数値型か文字列型かさえ判別できれば良いのならばA=V>0<3だけで良い(この場合は数値は1、文字列は0を返す)
 配列全体の比較演算はできないためSUFFIX(A[0])のように引数に添え字付きで記述する必要がある


《 数値、文字列関係な配列を初期化するERASE命令 》
DEF ERASE V
 VAR Z,Z$
 WHILE LEN(V)
  IF V[0]>0<3 THEN Z=POP(V) ELSE Z$=POP(V)
 WEND
END

 POP()で受け取る変数は数値型か文字列型かを判別すれば良いだけであって実数型か整数型かは判別する必要はないため型判別はこのように比較演算のみで簡単にできます。IF命令の部分はVARが左辺値になることを利用して1つにまとめて記述しても良いでしょう。


 配列をPOP()で受け取り要素数を1つずつ減らすことによって初期化を行うのではなく配列全体を代入が可能ということを利用して要素数0の配列を代入することで初期化を行うことも可能です。
 POP()を使用した方法だと要素数が数万、数10万になるとそれなりの時間(といってもせいぜい数秒程度)がかかりますが、要素数0の配列を代入する方法ならば瞬時に行えます。

VAR A[1000],B[0]
FOR I=0 TO 999
 A[I]=RNDF()
NEXT
A=B

 このように配列変数A[]に要素数が0のB[]を代入することでA[]の要素数を0にできますが、その場合は配列変数A[]B[]は参照型であるため同じ実データを参照します。そうなるとB[]の値が変化したらA[]にも影響があるという弊害があります。これは要素数0の配列C[]を用意してB=Cとすれば解決できますが、配列変数の初期化の度にダミーの要素数0の配列が必要となるため実用的な方法とはいえません。
 そこで便利なのがDEFで自作命令を定義することです。DEF内ではローカル変数が使えて同じ変数名であっても実行の度に初期化が可能なのでローカル変数として要素数0の配列変数を確保してそれを代入するだけで配列変数の初期化が可能です。

《 数値配列を初期化するINITR()関数 》
DEF INITR()
 VAR B[0]
 RETURN B
END

 この自作関数を用いてA=INITR()とすることで数値配列A[]は初期化が可能になります。(デフォルトでは実数型、OPTION DEFINT実行時は整数型に対応)
 戻り値として要素数0の配列を用意する必要があるためINITR Aのように命令として記述するのではなくA=INITR()のように関数として記述する必要があります。

 文字列配列は「文字列と数値は比較演算で3を返す」というのを利用すれば簡単に判別が可能なのですが、上記のPOP()を使った方法とは異なり、要素数0の配列を代入することで初期化を行う場合は実数型と整数型も判別する必要があるので注意が必要です。しかし、それは上記のSUFFIX()関数を使えば簡単です。

《 数値、文字列関係なく配列を初期化するINITA()関数 》
DEF INITA(A)
 IF !LEN(A) THEN RETURN A
 VAR V$[0],V%[0],V#[0]
 RETURN VAR("V"+"$%#"[SUFFIX(A[0])])
END
 ※動作させるには上記SUFFIX()関数が必要です。

 このINITA()関数ではA=INITA(A)で配列変数A[]を初期化できます。もちろん、A$=INITA(A$)で文字列配列、A%=INITA(A%)で整数型の数値配列を初期化できます。
 これを見てのように要素数が増えた場合に少し時間がかかるのが許容できるならば上記POP()を使った方法の方が自動型判別が簡潔に記述可能なことが分かるでしょう。しかし、この方法はPOP()を使った方法と比べると速度面のメリットがあるだけではなく下記のように更にワンステップ進んだ使い方も可能になるというメリットがあります。
 ここでは初期化(要素数0)が目的だったので要素数0の配列変数を代入していますが、任意の要素数の配列変数を代入すれば現在の要素数を任意の要素数に変化させるというVisualBasicのReDim命令相当のことも可能です。これは自作関数の引数を1つ増やしREDIM(A,N)のようにした場合にはVAR V$[N],V%[N],V#[N]とすれば任意の要素数Nの配列に変えることができるわけです。

 同じ変数名を2度入力するくらいならば型判別を自動でなくても手動の方が楽に感じるかもしれません。そこでサフィックス記号("$"、"%"、"#"のいずれか)を引数にして指定することでその型で初期化できるINITS()関数を作ってみました。

《 指定の型で配列を初期化するINITS()関数 》
DEF INITS(A$)
 VAR V[0],V$[0],V%[0],V#[0],Z$="V"+A$
 RETURN VAR(Z$)
END

 このINITS()関数はA=INITS("#")で配列変数A[]を実数型で初期化します。ちなみにサフィックス記号を省略してA=INITS("")のように記述した場合にはデフォルトでは実数型、OPTION DEFINT実行時には整数型で初期化されます。
 では、この変数名が示す型と初期化する型が異なる場合はどうかというと実はエラーが出ずに動作します。A=INITS("$")としてPUSH A,"ABC"としてみましょう。すると数値配列A[0]に文字列"ABC"が入っていることが分かります。  実はこれはプチコン3号の配列において要素数0の配列では参照渡しが行われているためです。そのためサフィックス記号に関係なく代入する側の型に変数が変わります。
 これが起きるのはDEFで定義した自作関数に限らず、VAR A[0],B$[0]の場合、A=B$で数値配列A[]が文字列変数として扱われるようになり、B$=Aで文字列配列B$[]が数値配列として扱われるようになります。

 特定の型で初期化可能ということはそれを有効活用すれば数値でも文字も入る万能変数にすることが可能になります。それを実現するのがINIT()関数です。

《 自由な型で変数を使用可能にするINIT()関数 》
DEF INIT(A)
 VAR V$[0],V%[0],V#[0],Z[0]
 Z=VAR("V"+"$%#"[SUFFIX(A)])
 PUSH Z,A
 RETURN Z
END
 ※動作させるには上記SUFFIX()関数、が必要です。

 VAR A[0]
 A=INIT("ABC")


 これでA[0]に文字列"ABC"が入ります。以降A[0]は文字列変数として使用可能になります。また、A=INIT(10)とすれば、A[0]は整数型となり、A=INIT(10.0)とすればA[0]OPTION DEFINTが記述されている場合であっても)実数型となります。サフィックス記号は何を付けても関係無いし付けなくても問題ありません。
 特に与えたい初期値がなくその型で初期化だけ行いたい場合は整数型ならばA=INIT(0)、実数型ならばA=INIT(0.0)、文字列型ならばA=INIT("")としてください。配列の初期化だけならばINITA()関数やINITS()関数でもできますが、このINIT()関数はすでに要素数1を確保してすぐに汎用の変数として使える状態になっているという点が異なります。INITA()関数やINITS()関数で初期化した場合は要素数が0になっているのでPUSHを使い変数の型に合わせて要素数を1増やす必要があり別途型判別が必要になるためそれが関数の中に内包されているか否かは使い勝手に大きな影響を与える)
 なお、最初に代入された時点で型が確定し、それ以降は再びINIT()関数で初期化しない限りは変更されません。これはグローバル変数で使用する場合はそれほどメリットはないですが、ローカル変数ならば文字列でも数値でも同じプログラムで処理が可能になる便利な関数を簡単に作ることが可能という大きなメリットがあります。(自作関数内で使用可能なローカルにおいて引数に使用される変数は代入された時点で型が決まるけどそれ以外は変数を宣言をする段階で型が決まってしまうため宣言をした後で自由に型の変更が可能なのがこのINIT()関数のメリットとなる)

 このようにプチコン3号で配列変数を使う場合や文字列変数を配列のように使う場合は「参照型」というのを知っておかないと思わぬ誤動作を招いてしまう場合もあるため注意が必要なのですが、その仕組みを理解すれば非常に便利であり有効活用が可能になるということが分かってもらえたことだと思います。ただし、初心者には難しい部分もあると思うのでまずは参照型というのがどのようなものかを実際に自分の手で確かめてみると良いと思います。


RETURN (プチコン3号講座のページにもどる) RETURN *MAIN (トップページにもどる)

inserted by FC2 system