MikoScript 言語仕様

 純粋配列

 純粋配列とは、データ型とサイズが全て同じ要素が、メモリー上に所定の順で連続して
配置されていて、整数のインデックスで各要素を特定する、従来型の配列です。純粋配列
は、連想配列と比較すると、柔軟性が低い反面、配列の要素数が多くても、各要素ごとに
メモリーのオーバーヘッドがあるわけではないので、大量の数値配列を効率的に扱えます。
また、要素データの内部のバイナリー構成が明確なので、それに基づいた処理が行えます。
 純粋配列は、生成される時に、その配列属性(要素のデータ型、次元数、各次元の要素
数)が決まり、その分のメモリーが割り当てられます。純粋配列の各次元の要素数は、各
要素の値を保持したまま、変更可能ですが、データ型と次元数は、各要素の値を保持した
ままでの変更はできません。
 純粋配列は、使用前に、所定の後置型関数(後述)を使って、生成しておきます。この
関数は、対象の箱の中身を指定された配列属性の純粋配列に設定します。純粋配列の箱は、
他の種類の箱と同様、任意に複製、移動、削除等が可能です。
 純粋配列の要素のデータ型は、現状、数値型のみに限定されています。この数値型には、
整数型が6種類、浮動小数点数型が2種類あります。これら以外のデータ型、たとえば、
文字列型等の配列には、連想配列を使うことになります。
 純粋配列への整数インデックスによるアクセスは、連想配列への連想名によるアクセス
よりも、やや高速です。

●純粋配列の生成
 純粋配列は、所定の後置型関数を使って生成します。以下にその一覧を示します。
    X 'BYTE( n, m, ... )     符号有 8-bit整数 を要素とする純粋配列を生成
    X 'UBYTE( n, m, ... )    符号無 8-bit整数       〃
    X 'SHORT( n, m, ... )    符号有16-bit整数       〃
    X 'USHORT( n, m, ... )   符号無16-bit整数       〃
    X 'LONG( n, m, ... )     符号有32-bit整数       〃
    X 'ULONG( n, m, ... )    符号有32-bit整数       〃
    X 'FLOAT( n, m, ... )   単精度浮動小数点数      〃
    X 'DOUBLE( n, m, ... )  倍精度浮動小数点数      〃
ここで、X は、生成される純粋配列を格納する箱を指定します。 ( n, m, ... ) は、
純粋配列の各次元の要素数を指定します。純粋配列の次元数は、この引数の個数になり
ます。例えば、
    ABC 'SHORT( 100 );
は、デフォールトスコープ内の ABC という名前の箱に、要素のデータ型が、符号有
16-bit 整数で、要素の数が 100 個の1次元の純粋配列を生成します。また、
    ::GDATA.FV 'DOUBLE( 30, 40 );
は、グローバルスコープ内の GDATA という箱の中の FV という箱に、データ型が倍精度
浮動小数点数で、要素の数が 30 x 40 個の2次元の純粋配列を生成します。

 純粋配列が生成される時に、その格納先の箱が存在しない場合、その箱は、新規に生成
されます。一方、その箱が既存の場合、その箱の中身は、すべて破棄されて、純粋配列に
変わります。但し、その既存の箱が、File や Buffer クラスのインスタンスの場合には、
事情が異なります。これに関しては後述します。

 純粋配列を格納する箱の指定には、参照を使うこともできます。たとえば、
    p := XYZ'new!;
    p'UBYTE( 20, 30, 40 );
とすれば、デフォールトスコープ内の箱 p の参照先の箱 XYZ が、符号無 8-bit
整数型の 20 x 30 x 40 個の要素の3次元の純粋配列に設定されます。

 純粋配列の各次元の要素数の指定( 上記の n, m, ... )は、定数でも変数でも、
構いませんが、データ型は、整数、または、浮動小数点数に限定されます。但し、
浮動小数点数の場合、小数点以下を切り捨てた整数値が採用されます。なお、純粋配列
の各次元の要素数の指定が無い場合、たとえば、X'LONG のような場合、純粋配列を生成
するのではなく、対象の箱に対する入出力形式の属性設定になります。これに関しては、
後述します。

 純粋配列を生成する上記の後置型関数は、正常にその純粋配列を生成できた場合、その
箱への参照を返します。一方、エラーになった場合は、null を返します。

 純粋配列の要素の各データ型のサイズと数値範囲は、以下の通りです。なお、FLOAT と 
DOUBLE の表現形式は、それぞれ、IEEE754規格の単精度と倍精度です。
    BYTE    8-bit    -128 〜 +127
    UBYTE    8-bit    0 〜 255
    SHORT   16-bit    -32768  〜 +32767
    USHORT  16-bit    0 〜 65535
    LONG   32-bit    -2147483648 〜 +2147483647
    ULONG   32-bit    0 〜 4294967295
    FLOAT   32-bit    ±1.175494351E-38 〜 ±3.402823466E+38
    DOUBLE  64-bit    ±2.2250738585072014E-308 〜 ±1.7976931348623158E+308
 純粋配列の要素の値は、生成された直後、全て 0 になります。純粋配列の次元数は、
現状、最大8次元までです。また、純粋配列の総データサイズ、つまり、各次元の要素数
の積と要素サイズ(バイト数)の積は、現状、最大 64 MB (メガバイト) までに制限され
ています。この制限のもとで、各要素の数は任意です。例えば、1次元なら、DOUBLE が
最大 8 メガ(8,388,608) 個まで、USHORT が最大 32 メガ(33,554,432) 個まで可能です。

●純粋配列の要素へのアクセス
 純粋配列の要素へは、各次元のインデックスを指定することによってアクセスします。
この表記形式は、以下のようになります。
    X( i, j, ... )
ここで、X は、純粋配列の箱の指定で、i, j, ... は、各次元のインデックス値の指定に
なります。たとえば、
    A(3) は、デフォールトスコープ内の純粋配列 A のインデックス 3 の要素、
    ::B(4,5) は、グローバルスコープ内の純粋配列 B のインデックス 4,5 の要素
となります。

 純粋配列のインデックスは、その純粋配列の各次元ごとに、数値で指定します。つまり、
n次元の純粋配列は、n個のインデックスの数値を指定する必要があります。

各次元のインデックス値は、0 が基点となり、以降1ずつ順に増え、最後は、その次元の
要素数よりも1少ない数になります。例えば、要素数が 10 個の1次元の純粋配列 
    C'BYTE(10)
の最初の要素は、C(0) で、次の要素は C(1)、以降、C(2), C(3), ... となって、最後の
要素は、C(9) となります。また、要素数が 3 x 5 個の2次元の純粋配列 
    D'SHORT(3,5)
の各要素は、
    D(0,0), D(0,1), D(0,2), D(0,3), D(0,4)
    D(1,0), D(1,1), D(1,2), D(1,3), D(1,4)
    D(2,0), D(2,1), D(2,2), D(2,3), D(2,4)
となります。

インデックス値の指定には、一般に式が使えますが、その評価値は、数値に限定されます。
その数値は、通常、整数にしますが、浮動小数点数でも可能です。浮動小数点数の場合、
小数点以下を切り捨てた整数値が、実質のインデックス値になります。

インデックス値が、上述の範囲内に入っていない場合、または、数値でない場合、または、
インデックスの指定個数が次元数と合わない場合には、例外が発生します。例外に関して
は、「例外処理」の章で説明しています。

●純粋配列の要素への代入
 純粋配列の要素への代入は、通常の代入と同様に、代入演算子を使います。例えば、
    A(i,j) = 5;
は、純粋配列 A のインデックス i,j の要素に 5 を代入します。

純粋配列の要素への代入では、右辺はどのような演算式でもかまいませんが、その結果は、
数値に限定されます。数値以外の場合は、例外が発生します。

右辺の数値は、基本データ型の整数値か、浮動小数点数値のどちらかです。この数値は、
左辺の純粋配列の要素のデータ型に応じて、以下のように代入されます。

・右辺が整数値(32-bit)の場合
    BYTE, UBYTE  の要素へは、右辺の整数値の下位 8-bit が代入されます。
    SHORT, USHORT の要素へは、右辺の整数値の下位 16-bit が代入されます。
    LONG, ULONG  の要素へは、右辺の整数値がそのまま代入されます。
    FLOAT, DOUBLE の要素へは、右辺の整数値を、それぞれ、単精度と倍精度の
                                浮動小数点数値に変換した値が代入されます。
・右辺が浮動小数点数値(倍精度)の場合
    BYTE, UBYTE, SHORT, USHORT, LONG, ULONG の要素へは、
            右辺の浮動小数点数値の小数点以下を切り捨てた整数値(32-bit)が、
            上述の右辺が整数値の場合と同様に、代入されます。
    FLOAT の要素へは、 右辺の浮動小数点数値を単精度に変換した値が代入されます。
    DOUBLE の要素へは、右辺の浮動小数点数値がそのまま代入されます。

純粋配列の要素への代入に使う代入演算子は、通常、= ですが、:= かまたは <- を
使うこともできます。どの代入演算子でも結果は同じです。

純粋配列の要素は、以下の演算併用の代入演算子の左辺(代入先)になれます。
    +=  -=  *=  /=  %=  &=  |=  ^=  <<=  >>=
但し、浮動小数点数値の要素に対して、ビット操作はできません。

●純粋配列の要素の値
 純粋配列の要素の値が、右辺式(代入先でない式)で使われる時は、その値が整数型
( BYTE, UBYTE, SHORT, USHORT, LONG, ULONG )なら、基本データ型の整数に変換され、
浮動小数点数型( FLOAT, DOUBLE )の数値なら、基本データ型の浮動小数点数に変換さ
れます。変換後の基本データ型の数値の扱いに関しては、「基本データ型」や「演算子」
等の章で述べている通りです。

 この変換において、BYTE, UBYTE, SHORT, USHORT の要素の数値範囲は、基本データ型
の整数値範囲内に入るので、特に問題はありません。また、FLOAT の要素も、基本データ
型の浮動小数点数値の範囲と精度内に入るので、特に問題はありません。LONG と DOUBLE 
のデータ型は、それぞれ、基本データ型の整数型と浮動小数点数型と同じなので、実質的
には無変換です。
 しかし、ULONG の要素の値から、基本データ型の整数値への変換では、両者の 32-bit 
のビットパターンが全く同じになるため、0x8000000 〜 0xFFFFFFFF の範囲の数値では、
両者の値の解釈が異なります。つまり、ULONG の 2147483648 〜 4294967295 の数値は、
基本データ型の整数では -2147483648 〜 -1 の数値として解釈されます。

 純粋配列の要素への参照はありません。参照は、あくまで、箱に対するものです。その
ため、純粋配列の要素が、関数の引数になる場合、上述のように変換された実値が渡され
ます。なお、関数が純粋配列の要素を返す場合、言うまでもありませんが、同様に、実値
が返されます。

●純粋配列の各要素の並び方
 純粋配列の要素データを格納しているメモリー領域には、各要素が連続して、つまり、
各要素間の隙間なしに(各要素が隣接して)、配置されています。以下では、各要素が、
具体的にどのような順で配置されているのかを説明します。この配置順は、初期化データ
の設定や、要素データのバイナリー構成に依存した処理を行なうときに、考慮する必要が
あります。

 まず、1次元の純粋配列では、各要素は、インデックスの順に配置されています。
例えば、A'BYTE(5) では、
    A(0), A(1), A(2), A(3), A(5)
の順になります。

 次に、2次元の純粋配列( i, j )では、第2次元のインデックス j の順に配置された
要素の列(ブロック)が、第1次元のインデックス i の順に配置されています。
例えば、B'BYTE(3,4) では、
    B(0,0), B(0,1), B(0,2), B(0,3)
    B(1,0), B(1,1), B(1,2), B(1,3)
    B(2,0), B(2,1), B(2,2), B(2,3)
の順になります。

 一般に、N 次元の純粋配列( i, ..., l, m, n )では、第 N 次元のインデックス n の
順に配置された要素の列(ブロック)が、第 N-1 次元のインデックス m の順に配置され、
そのブロック列が、第 N-2 次元のインデックス l の順に配置されて、以降、同様にして、
最後は、第1次元のインデックス i の順に配置されています。
たとえば、C'BYTE(2,3,4) では、
    C(0,0,0), C(0,0,1), C(0,0,2), C(0,0,3)
    C(0,1,0), C(0,1,1), C(0,1,2), C(0,1,3)
    C(0,2,0), C(0,2,1), C(0,2,2), C(0,2,3)
    C(1,0,0), C(1,0,1), C(1,0,2), C(1,0,3)
    C(1,1,0), C(1,1,1), C(1,1,2), C(1,1,3)
    C(1,2,0), C(1,2,1), C(1,2,2), C(1,2,3)
の順になります。ちなみに、これを、この順にプリントするプログラムは、以下のように
なります。
    for( i = 0 ; i < 2 ; i++ )
        for( j = 0 ; j < 3 ; j++ )
            for( k = 0 ; k < 4 ; k++ )
                print C( i, j, k );
●初期化データの設定(1次元の場合)
 純粋配列の各要素に対して、初期化データを設定することができます。この表記形式は、
以下のようになります。
    純粋配列 = {  初期化データ列  };
ここで、「初期化データ列」は、各要素値をコンマで区切った並びになります。各要素値
の並び順は、前節「純粋配列の各要素の並び方」で述べた順になります。例えば、
    A'SHORT(5) = { 1, 2, 3, 4, 5 };
とすると、A(0),A(1),...,A(4) に、それぞれ、1, 2, ..., 5 が代入されます。

 初期化データの設定対象の純粋配列は、初期化データの設定によって、新規生成される
のではなく、既に生成されている純粋配列に対して、初期化データの設定が行なわれます。
例えば、上例の純粋配列 A では、'SHORT(5) によって新規に生成されて、その生成済の
純粋配列に対して、初期化データ { 1, 2, 3, 4, 5 } が設定されます。そのため、初期
化データの設定は、最初の1回だけでなく、必要に応じて、何度でも行なえます。

 初期化データ列内の各要素値は、必ずしも定数である必要はなく、一般的に、式にする
ことができます。例えば、
    x = 100;
    function f()  { return 200; }
    B'LONG(3) = {  x, f(), x + f()  };
を実行すると、B(0),B(1),B(2) は、それぞれ、100, 200, 300 となります。

 初期化データ列内の各要素値のデータ型は、数値に限定されます。もし数値でなければ、
例外が発生します。初期化データ列内の各要素値のデータ型が、純粋配列の要素のデータ
型へ、どのように代入されるかは、前節「純粋配列の要素への代入」で述べた通りです。

 初期化データ列の各要素値は、省略することができます。その場合、その要素には何も
代入されません。つまり、その要素の値は、前のままになります。例えば、上記のように
初期化データが設定された純粋配列 A に対して、
    A = { , , -3, , -5 };
を実行すると、A(0),A(1),...,A(4) は、それぞれ、1, 2, -3, 4, -5 となります。

 初期化データ列内の要素値の個数は、純粋配列の要素数よりも、少なくても構いません
が、多いと例外が発生します。少ない場合、残りの要素の値は、設定されず、前のままに
なります。例えば、上記の純粋配列 A に対して、
    A = { 11, 12, 13 };
を実行すると、A(0),A(1),...,A(4) は、それぞれ、11, 12, 13, 4, -5 となります。
しかし、
    A = { 11, 12, 13, 14, 15, 16 };
を実行すると、例外が発生します。

 初期化データ列内の最後の要素値の後のコンマは無視されます。例えば、
    A = { 11, 12, 13, 14, 15, };
では、最後の要素値 15 の後にコンマがありますが、このコンマは無視されます。

●初期化データの設定(2次元以上の場合)
 2次元以上の純粋配列の場合も、同様に、初期化データ列内に、各要素の値を、各要素
の順に並べれば、初期化データが設定されます。例えば、
    C'BYTE(2,3) = { 11, 12, 13, 21, 22, 23 };
を実行すると、初期化データ列内の各要素値は、それぞれ、C(0,0),C(0,1),C(0,2), 
C(1,0),C(1,1),C(1,2) へ代入されます。

 2次元以上の純粋配列の場合には、各次元の要素値の並びのブロックを波括弧 {  } で
囲って、入れ子にすることができます。例えば、上記の場合、
    C'BYTE(2,3) = { { 11, 12, 13 }, { 21, 22, 23 } };
と記述できます。また、3次元の場合、
    D'SHORT(2,3,4) =
    {
      { { 111,112,113,114 }, { 121,122,123,124 }, { 131,132,133,134 } },
      { { 211,212,213,214 }, { 221,222,223,224 }, { 231,232,233,234 } },
    };
のように記述できます。

 初期化データ列の {  } 内の要素値は、前述と同様に、省略できます。その場合、その
要素の値は、設定されず、前のままになります。例えば、先程の純粋配列 C に対して、
    C = { { , , -13 }, { -21, -22,  } };
とすると、C(0,2), C(1,0), C(1,1) が、それぞれ、-13, -21, -22 に設定されるだけで、
他の要素は、前の値のままになります。また、初期化データ列内の {  } のブロックも、
省略できます。その場合、そのブロックに対応する全要素の値は、設定されず、前のまま
になります。例えば、
    C = { , { 21, 22, 23 } };
とすると、C(1,0), C(1,1), C(1,2) が、それぞれ、21, 22, 23 に設定されますが、他の
要素は前の値のままです。

 初期化データ列内の {  } 内の要素値は、前述と同様に、そのブロックの要素数よりも、
少なくてもかまいませんが、多いと例外が発生します。少ない場合、残りの要素の値は、
設定されず、前のままになります。

●純粋配列以外の箱への初期化データの設定
 純粋配列の箱への初期化データの設定は、以上述べた通りですが、純粋配列ではない箱
へ初期化データを設定した場合、その箱は、連想配列の要素を格納する箱にされて、その
初期化データの各要素値は、その箱内の連想配列の各要素へ設定されます。

 但し、その箱の属性として、純粋配列のデータ型( BYTE, UBYTE, SHORT, USHORT,
LONG, ULONG, FLOAT, DOUBLE )の入出力形式が設定されている場合には、その箱は、同
データ型の純粋配列として、再形成され、その各要素に、初期化データの各要素値が設定
されます。たとえば、
    E'LONG = { 1, 2, 3, 4, 5, 6, 7, 8 };
とすると、箱 E は、まず、'LONG の後置型関数によって、LONG 型の入出力形式の属性が
設定されますが、次に、初期化データによって、LONG 型8個の要素の1次元の純粋配列
として再形成されます。このようにして形成される純粋配列は、1次元に限定されます。
また、その要素数は、初期化データの要素値の個数になります。

 なお、連想配列への初期化データの設定に関する詳細は、「連想配列」の章で説明して
います。

●純粋配列の配列属性(データ型、次元数、要素数)の変更
 純粋配列の「各次元の要素数」は、各要素の値を保持したまま、任意に変更可能です。
例えば、
    F'DOUBLE(2,3) = { { 1.1, 1.2, 1.3 }, { 2.1, 2.2, 2.3 } };
の純粋配列に対して、
    F'DOUBLE(3,4);
を実行すると、各要素の値は、以下のようになります。
    1.1, 1.2, 1.3, 0.0,
    2.1, 2.2, 2.3, 0.0,
    0.0, 0.0, 0.0, 0.0,
次に、
    F'DOUBLE(2,2);
を実行すると、各要素の値は、以下のようになります。
    1.1, 1.2,
    2.1, 2.2,
 一方、純粋配列の要素の「データ型」と「次元数」は、各要素の値を保持したままでは、
変更できません。もし、変更した場合には、もとの純粋配列の内容は消失します。
例えば、上記の純粋配列 F に対して、
    F'SHORT(10);
を実行すると、各要素の値は、全て 0 になってしまいます。

●純粋配列の破棄
 純粋配列は箱に格納されているので、その箱が破棄されると、その純粋配列も破棄され
ます。その時、それに割り当てられていたメモリーは全て解放されます。
 純粋配列の箱 A を明示的に破棄するには、以下のようにします。
    delete A;
 また、一般に、あるスコープ内にある箱は、そのスコープが消滅する時に破棄されます
が、純粋配列の箱も同様で、たとえば、関数ローカルスコープ内にある純粋配列は、その
関数から抜ける時に、自動的に破棄されます。

●純粋配列の要素データ上の文字コード列の操作
 純粋配列の要素データのメモリー領域は、各要素が連続して配置されているので、そこ
に文字コード列を格納すると、各文字コードは、純粋配列のインデックスでアクセスでき
るようになります。この用途に、以下の2つのリレー型関数が用意されています。

  X'puts( s, i, j, ... )
 純粋配列 X のインデックス i, j, ... からの各要素へ文字列 s の各文字コードを
 設定します。なお、i, j, ... はそれぞれ省略されると、0 として扱われます。
 文字列 s の0終端値は設定されません。この関数の返値は、正常時、設定した要素数、
 つまり、設定した字数(バイト数ではない)になります。異常時は、null になります。
 対象の純粋配列のデータ型は整数に限ります。

  X'gets( n, i, j, ... )
 純粋配列 X のインデックス i, j, ... から n 個の要素の文字コード列を取得します。
 なお、n が省略/負値の時、0値の要素までになります。 i, j, ... はそれぞれ省略
 されると、0 として扱われます。この関数の返値は、正常時に、取得した文字列になり、
 異常時に、null になります。対象の純粋配列のデータ型は整数に限ります。

 例えば、T'UBYTE(10) の純粋配列に対して、
    T'puts( "ABCDEFGH" );
とすると、T(0),T(1),T(2),... は、それぞれ、`A`,`B`,`C`,`D`,`E`,`F`,`G`,`H`, 0, 0
になります。次に、
    T(2) = `x`;
    T(3) = `y`;
    print T'gets;
とすると、"ABxyEFGH" がプリントされます。続いて、
    T'puts( "PQR", 4 );
    print T'gets;
とすると、"ABxyPQRH" がプリントされます。また、
    print T'gets( 2, 5 );
とすると、"QR" がプリントされます。

 2バイトの文字コードを扱う場合は、対象の純粋配列のデータ型は、USHORT にする
必要があります(それ以上のサイズにしても構いません)。例えば、H'USHORT(8) の
純粋配列に対して、
    H'puts( "あいうABC" );
とすると、H(0),H(1),H(2),... は、それぞれ、`あ`,`い`,`う`,`A`,`B`,`C`, 0, 0 に
なります。

●注意事項
 純粋配列を生成する後置型関数は、入出力形式を設定する場合にも使います。この両者
を区別するのは、関数の引数の有無です。純粋配列を生成する場合は、関数名の次に引数
が必要ですが、入出力形式を設定する場合は、関数名の次に引数を付けません。例えば、
    A'USHORT( 100 );    // 純粋配列の生成 (引数有り)
    B'USHORT;           // 入出力形式の設定(引数無し)
 ただし、File と Buffer クラスのインスタンス箱に対しては、入出力形式を設定する
場合でも、関数名の次に引数が付きます。たとえば、
    buf = ::Buffer( 1000 );
で生成した Buffer クラスのインスタンス箱 buf に対して、
    buf'BYTE( 256 );
は、箱 buf を、純粋配列として再生成するのではなく、入出力形式として 256 個の BYTE 
を設定することになります。

 入出力形式の詳細は、「入出力形式」の章で説明しています。ファイルとバッファに関
しては、それぞれ、「ファイル操作」と「バッファ操作」の各章で詳説しています。