配列

ここでは、複数の値を格納できる変数である、配列について解説します。

- 目次 -

配列とは

複数の値を一つの変数名で表現したい場合には、配列を使用します。

例えば、プログラムによって、100個の点をアニメーションで動かす必要があったとしましょう。 そこでもし、各点の x 位置と y 位置を、それぞれ 1 つずつ変数として宣言すると、合計200個もの変数が必要になってしまいます。 また、それぞれの変数に、値を計算して格納する処理も、200組書かないといけません。それ自体も大変な作業ですし、個数を変える時もまた大変です。

そんな場合に、各点の x 位置や y 位置を、それぞれ配列として宣言しておけば、全点の x 位置や y 位置をまとめて格納できて便利です。 また、後で実際に説明するように、for 文などの制御構文を使って、各点に対する処理をまとめて書く事も容易になります。

一般に、プログラムで大量のデータを処理する場合には、配列(言語によってはリストなど)をうまく活用する事がとても重要です。

配列の宣言

複数の値を一つの変数名で表現したい場合には、配列を使用します。

配列を宣言するには、以下のように記述します:

データ型   配列名[ 要素数 ] ;

ここで要素数は、配列に格納できる値の個数を指定します。なお、VCSSLでは、以下のようにデータ型の部分に要素数を付ける記述も可能です:

データ型[ 要素数 ]   配列名 ;

これら2 通りの宣言方法は、どちらも全く同じ処理となります。

例として、int 型の値を格納する配列を用意してみましょう:

DeclareIntArray.vcssl

または、

DeclareIntArrayType.vcssl

とします。これで5 つの整数値を格納できる配列が用意できました。 なお、後者のように、データ型に要素数を付ける表記は、C言語ではエラーになるので要注意です。VCSSLは、基本的にはC言語に似ていますが、配列については少し仕様が違う部分があります。

配列に値を代入したり、呼び出したりするには、変数名の右に [ インデックス番号 ] をつけて、 普通の変数と同じように行う事が可能です:

ArrayInput.vcssl

- 実行結果 -

1

これを実行すると 1 が表示されます。つまりa の中の、3 番目の要素に値を格納したわけです。

インデックス番号は、配列の要素に割り振られる番号で、 0 番から(要素数-1)番までが用意されています。要素数番まででは無い事に注意してください。 例えば、上の例では0,1,2,3,4 番までが用意されており、5 番は存在しません。

配列はその用途から、for 文などの、繰り返しの制御構文と併用される事が多いでしょう。 特に、要素数が大きい配列データを処理する場合には、各要素に対する処理を1つずつ書くのは非現実的なため、for 文をうまく使う事が重要です。 簡単な例として、0 から10 までの整数の2 乗を、配列に格納してみましょう:

ArrayAndControl.vcssl

- 実行結果 -

64

これを実行すると 64 が表示されます。 このように「 たくさんの値を計算して配列に格納しておいて、後でその結果にアクセスしながら使う 」と言った場面はデータ処理でよくあります。

多次元配列

配列は、インデックスを複数付ける事もできます。これを多次元配列と呼びます。

多次元配列の宣言・使用方法は 1 次元の場合と基本的に同じで、 [ ] を複数付けるだけです:

Array2DInput.vcssl

- 実行結果 -

16

これを実行すると 16 が表示されます。

上の例の a は 2 次元の配列ですが、 [ ] の数を増やすだけで何次元でも利用可能です。

配列の要素数を取得する

プログラムの実行中に、配列の要素数を調べたい場合はよくあります。そういう場合には、length 関数を使用します。

length 関数は、配列の指定された次元の要素数を調べて、結果をint型で返します。 最初の引数に配列変数を、次の引数に次元の番号を指定します。 次元の番号は、配列の左から 0 次元目, 1 次元目, 2 次元目, ...と数えます。

GetArrayLength.vcssl

- 実行結果 -

3

これを実行すると 3 が表示されます。 これは a の左端から数えた2 番目(0, 1, 2 と数える)、つまり右端の次元の要素数です。 同様にlength( a, 0 )は 1 を、length( a, 1 )は 2 を返します。

第 2 引数の省略は非推奨

なお、 length 関数で 2 つめの引数を省略すると、各次元の要素数をまとめた配列が返されます。

上の例では、length(a) とすると、要素数3 のint 配列が返されます。 しかし、この機能は古い互換性維持のためのもので、配列の次元を左からではなく、右から 0, 1, 2 と数える仕様となっているため、混乱を招くという問題があります。

従って、length 関数で2つめの引数を省略するのは、現在では非推奨となっています。

配列の要素数を変更する

配列の要素数を変更したい場合は、alloc 関数を使用します。

alloc( 配列, 0次元目の要素数, 1次元目の要素数, ... ) ;

この関数は、最初の引数に指定された配列の要素数を変更します。なお、配列の次元は、ここでも先ほどと同様、左から 0 次元目, 1 次元目, 2 次元目, ... と数えます。

具体的に使用してみましょう。以下のように記述し、実行してみてください:

ArrayAllocFunction.vcssl

- 実行結果 -

20

これを実行すると 20 が表示されます。 このように、alloc 関数によって、配列変数 a の要素数を、10 から20 に変更できました。

なお、alloc 関数によるサイズ変更の前後では、配列の各要素の値は維持されます。 つまり、もし上で a[ 1 ] に最初 255 が入っていたとすると、 サイズ変更後の a[ 1 ] も255 のままです。多次元の場合でも同様で、 同じインデックスに対応する要素は、同じ値に保たれます。

配列の全要素に対する代入と演算

このセクション以降の内容は、多くのC言語系のプログラミング言語ではサポートされておらず、エラーとなってしまいます。そのため、VCSSLに特有の配列操作だという事を、想定した上で読みください。

いくつかの、科学技術計算を重視した言語やライブラリでは、似たような配列操作がサポートされている事があります。これは、行列やテンソルなどの計算を直感的に記述するためです。VCSSLも、同じ意図で、これから述べる配列操作をサポートしています。

まず、配列の全要素に同じ値を代入したい場合には、配列のインデックス番号を指定せずに、

配列 = 値 ;

といった処理が可能です。例えば、

InputScalarToArray.vcssl

これで a の全要素に1 が代入されます。

- 実行結果 -

1

また、配列の全要素に、同じ値で同じ演算(加算など)を行いたい場合は、いちいちループを回さなくても、以下のように単純に書けます:

AddScalarToArray.vcssl

- 実行結果 -

3

上の処理の「 a = a + 2 」では、配列 a の全要素に 2 が加算され、3 となります。「 a += 2 」と書く事もできます。

要素数1の配列を、配列でない変数に代入する

逆に、配列を、配列でない普通の変数に代入する事は、一般にはできません。

しかし例外があります。それは配列の要素数が 1 の場合です。 要素数が 1 の配列は、データ的には普通の変数と変わらないので、普通の変数へ代入する事ができます:

InputToArrayScalar.vcssl

- 実行結果 -

1

上の例を実行すると 1 が表示されます。

配列同士の代入

配列に別の配列を代入した場合は、要素数と、各要素の値が同じ配列になります。以下がその例です:

ArrayInputSIMD.vcssl

- 実行結果 -

2

このプログラムを実行すると2 が表示されます。

要素数の異なる配列を代入する場合には、少し注意が必要です。 そういった場合、代入先の配列の要素数が変更され、代入したものと同じになります。

ArrayInputSIMDDiffLength.vcssl

- 実行結果 -

2

このプログラムを実行すると、結果は「 2 」が表示されます。そして、a の要素数は20 になります。

C言語系の他言語との違いには注意が必要

ところで、多くのC言語系の他言語では、配列の代入は参照(アドレス)の代入となります。 そのため、代入先(a)と代入元(b)の配列は、同じデータ領域を参照するようになり、同一の配列のように振舞うようになります。 この場合、代入後に、代入先(a)の配列に変更を加えると、それが代入元(b)の配列にも反映されます。

一方、VCSSLでは、既に見たように、配列の代入は全要素値のコピーとなります。同じデータ領域を参照するようにはなりません。 従って、代入後に、代入先(a)の配列に対して変更を加えても、それは代入元(b)の配列に反映されません。

VCSSLは基本的に、C言語系の他言語となるべく同じ要領で扱えるようになっていますが、このような配列関連の仕様の違いについては注意が必要です。