4.高度な演算


4−1.整数と小数と文字列

SphereScriptは整数と小数と文字列を明確に分けて扱っています。 「100」と「100.0」と「"100"」は全く別のものとなります。

1÷2は0.5ですが、

===============
x = 1/2;
===============

と書くとxには0が入ります。それは、単純に整数同士を計算させると、結果も整数となり、小数点以下が切り捨てられてしまうからです。小数であることを明示したい場合は以下のように記述してください。

===============
x = 1.0/2.0;
===============

こうすることで、小数同士の割り算となり、結果も小数となります。演算のどちら一方が小数であれば結果は小数になります。

===============
x = 1/2.0;
===============

と書いても、

===============
x = 1.0/2;
===============

と書いても結果は同じです。

文字列と数値を加算すると自動的に文字列変換されますが、そのときの値が小数の場合、有効数字の末尾まで表示されます。

===============
print_string( 7 + "÷" 2 + "=" + 7.0/2.0 + "です。\n");
===============

上記の結果「7÷2=3.50000です。」と表示されます。


4−2.複雑な演算

算数では括弧の中が最優先で計算され、次に足し算より掛け算が優先して計算されます。 同様に、SphereScriptでも括弧及び積算の優先順位が適用されます。

===============
x=(4+6*(4-2))/8;
===============

上記の結果、xには2が代入されます。

小数と整数の違いも演算と同時に評価されます。

===============
x=10/3+10/3.0;
===============

左側の「10/3」は「3」になり、右側の「10/3.0」は「3.33333」になります。 その結果を加算するのでxには「6.33333」が代入されます。


4−3.変数の宣言と明示的型キャスト

今までの説明では、整数が勝手に小数に切り替わって、変数に代入される値が整数なのか小数なのか文字列なのか、プログラムをよく見ないとはっきりしません。ここで「何が入っているのか?」ということを『型』という言葉で表現します。 1や-85などは『整数型』、3.14や10.00は『小数型』、"abc"や"123"は『文字列型』です。 以下のようにすると、変数に代入される値の型を指定できます。

=======================
int i;      //変数iは常に整数のみ代入されます。intは整数型を表します。
float f;    //変数fは常に小数のみ代入されます。floatは小数型を表します。
string s;   //変数sは常に文字列のみ代入されます。stringは文字列型を表します。

i=10*2.45;  //24.5が整数に変換されて、24が変数iに代入されます。
f=3*5;      //15が小数に変換されて、15.0が変数fに代入されます。
s=9*9;      //81が文字列に変換されて、"81"が変数sに代入されます。
=======================

上記の記述を『変数の宣言』と呼びます。 変数の宣言に使用できる構文は以下の通りです。初期値の指定は省略可能です。

int 変数名=初期値;      //整数型の変数を宣言する
float 変数名=初期値;    //小数型の変数を宣言する
string 変数名=初期値;   //文字列型の変数を宣言する
variable 変数名=初期値; //可変型の変数を宣言する
function 変数名=初期値; //関数型の変数を宣言する

※配列については上記の例に含まれません。配列の項で解説します。 初期値には数値や文字列だけでなく、計算式が指定できます。 variableは代入される値により自動的に型変換される変数です。 型の宣言をしないで変数を使用すると自動的にvariable型になるので、明示的にグローバル変数を宣言していることを記述上明確にする以外の目的で使うことはほとんどありません。 関数の引数で使うことがほとんどです。『グローバル変数』および『関数の引数』については関数の項で解説します。 また、functionは関数型変数の項で解説します。

変数の宣言で型を指定しても、あくまで代入される瞬間に自動的に変換される仕様なので、以下のようにしても、正しい結果が得られません。

=======================
float f;
f=10/3;     //「10/3」が整数同士の除算として先に計算されてしまうので、3.33333ではなく3.0が変数fに代入されてしまう。
=======================

代入する瞬間以外でも型を変換したい場合は、以下のようにして下さい。

=======================
int a=10;               //変数aは整数型になる
int b=3;                //変数bは整数型になる
f=(float)a/(float)b;    //変数aとbの内容を小数型とみなして計算するため、3.33333が変数fに代入される。
=======================

上記を『明示的型キャスト』とまたは単に『型キャスト』呼びます。明示的型キャストでは変数の型そのものを変更するのではなく、あくまで計算途中の値の型を変換します。 そのため上記の例で変数aとb自体は依然として整数型のままです。

明示的型キャストで使用できる構文は以下の通りです。
(int)値     //整数型への明示的型キャスト
(float)値   //小数型への明示的型キャスト
(string)値  //文字列型への明示的型キャスト

なお、宣言した変数の型にあわせて自動的に型が変わることを『暗黙の型キャスト』と呼びます。


4−4.配列

例えば4人分の名前を扱う変数として、以下のように記述したとします。

===============
name1="ハヤト";
name2="オリバー";
name3="ジャック";
name4="モミジ";
===============

これでは全員の名前を表示したいときに、以下のように書かなければいけません。

===============
print_string(name1 + "\n");
print_string(name2 + "\n");
print_string(name3 + "\n");
print_string(name4 + "\n");
===============

人数が増えたときに毎回プログラム全てを修正しなければいけないので大変です。 そこで、以下のようにすると一つの変数名に番号をつけて複数の値を代入できます。

===============
name[0]="ハヤト";
name[1]="オリバー";
name[2]="ジャック";
name[3]="モミジ";
===============

このときのnameを『配列変数』または単に『配列』と呼び、変数に番号をつけて別々の値を代入することが出来るようになります。 なお、上の[0]や[1]を『添え字』と呼びます。 これだけでは大して変わりませんが、表示するときにループで処理できます。

===============
for (i=0;i<4;i++) {
    print_string(name[i] + "\n");
}
===============

上記のように、添え字の中に変数を指定することが可能です。変数だけでなく、式も指定できます。ただし添え字として指定できるのは整数のみです。 4件の場合はあまり恩恵がありませんが、100件以上の敵ユニットを制御するプログラムを書こうとすると、このような配列が無いと大変苦労します。100行コピペで書くのはちょっと非常識です。

配列は2重3重に使用できます。

===============
for (i=1;i<=9;i++) {
    for (j=1;j<=9;j++) {
        kuku[i][j]=i*j;    //配列に九九の結果を代入
    }
}
===============

2重の配列のことを『二次元配列』と呼びます。3重ならば『三次元配列』です。上のname[i]は配列が一つなので『一次元配列』と呼びます。 数値計算などの場面では、一次元配列のことを『ベクトル』、二次元以上の配列のことを『行列』と呼ぶことがあります。 元々は線形代数の用語ですが、このような呼び方を覚えておくと良いでしょう。 なお、iやjをゼロ次元配列と呼ぶことはありません。単なる変数です。ただし、ベクトル、行列という呼び方と同様に、これを『スカラー』と呼ぶことがあります。いずれも数学用語です。

変数の型宣言が出来るように、配列の変数の型宣言が可能です。

===============
string name[4];
int kuku[10][10];
===============

詳しく書くと

型 変数名[要素数];                          //一次元配列の宣言
型 変数名[要素数][要素数];                  //二次元配列の宣言
型 変数名[要素数][要素数][要素数];          //三次元配列の宣言
型 変数名[要素数][要素数][要素数][要素数];  //四次元配列の宣言

というフォーマットです。現時点では初期値の代入には対応していません。必ずゼロで初期化されます。また、4次元が最大の配列です。 注意したいのが要素数で、配列はゼロからカウントされるため、上記の場合name[3]までしか使えません。 name[4]を使おうとすると、宣言されていない変数となり、string型として解釈してくれなくなるので注意してください。 上記の場合、配列kukuもkuku[9][9]が最も大きい添え字です。

当然ですが、宣言した配列はその掛け算の分だけメモリを消費しますので、以下のような宣言をした途端、変数管理のシステムが容量オーバーを起こしてエラーとなってしまいます。

===============
float pos[1024][1024][100][4];  //SphereScriptの変数だけで1.6GBのメモリなんて管理できません!
===============

4−5.配列の代入と計算

変数に配列を丸ごと代入することが出来ます。

===============
name_list=["ハヤト", "オリバー", "ジャック", "モミジ"];
===============

このようにすると以下のような記述と同等の結果になります。

===============
name_list[0]="ハヤト";
name_list[1]="オリバー";
name_list[2]="ジャック";
name_list[3]="モミジ";
===============

配列を配列として変数に代入することも出来ます。 また配列の代入時には値だけでなく式も使えます。

===============
kuku_row0=[0*0, 0*1, 0*2, 0*3, 0*4, 0*5, 0*6, 0*7, 0*8, 0*9];
kuku_row1=[1*0, 1*1, 1*2, 1*3, 1*4, 1*5, 1*6, 1*7, 1*8, 1*9];
kuku_row2=[2*0, 2*1, 2*2, 2*3, 2*4, 2*5, 2*6, 2*7, 2*8, 2*9];
kuku_row3=[3*0, 3*1, 3*2, 3*3, 3*4, 3*5, 3*6, 3*7, 3*8, 3*9];
kuku_row4=[4*0, 4*1, 4*2, 4*3, 4*4, 4*5, 4*6, 4*7, 4*8, 4*9];
kuku_row5=[5*0, 5*1, 5*2, 5*3, 5*4, 5*5, 5*6, 5*7, 5*8, 5*9];
kuku_row6=[6*0, 6*1, 6*2, 6*3, 6*4, 6*5, 6*6, 6*7, 6*8, 6*9];
kuku_row7=[7*0, 7*1, 7*2, 7*3, 7*4, 7*5, 7*6, 7*7, 7*8, 7*9];
kuku_row8=[8*0, 8*1, 8*2, 8*3, 8*4, 8*5, 8*6, 8*7, 8*8, 8*9];
kuku_row9=[9*0, 9*1, 9*2, 9*3, 9*4, 9*5, 9*6, 9*7, 9*8, 9*9];
kuku=[kuku_row0, kuku_row1, kuku_row2, kuku_row3, kuku_row4,
      kuku_row5, kuku_row6, kuku_row7, kuku_row8, kuku_row9];
===============

上記は配列の入った変数をさらに配列にしてkukuに代入しています。

===============
    print_string("3x4=" + kuku[3][4]);
===============

というように代入後は通常の2次元配列として扱うことが出来ます。 以下のようにすれば、2次元以上の配列もそのまま代入できます。

===============
num_list=[[0,0,0], [1,2,3], [2,4,6]];
===============

配列の内容を加減算することが出来ます。

===============
list1=[2,5,8,3];
list2=[1,2,3,4];
list3=list1+list2;
===============

上記のようにすることで、配列の内容を加算できます。結果、list3の内容は[3,7,11,7]となります。 これは以下のような記述と同等です。

===============
list1=[2,5,8,3];
list2=[1,2,3,4];
list3[0]=list1[0]+list2[0];
list3[1]=list1[1]+list2[1];
list3[2]=list1[2]+list2[2];
list3[3]=list1[3]+list2[3];
===============

加算だけでなく、減算ももちろん可能です。上の例では全て整数にしていますが整数である必要はありません。

===============
list1=[1.4, 0.4, 2.4, 3.1];
list2=[1.1, 2.4, 0.3, 2.0];
list3=list1-list2;
===============

list3の内容は[0.3, -2.0, 2.1, 1.1]となります。

乗算及び除算では配列同士ではなく、配列と配列ではない値同士の計算となります。

===============
list1=[2,5,8,3];
list2=list1*2;
===============

これは以下のような内容と同じです。

===============
list1=[2,5,8,3];
list2[0]=list1[0]*2;
list2[1]=list1[1]*2;
list2[2]=list1[2]*2;
list2[3]=list1[3]*2;
===============

上記の結果は[4,10,16,6]となります。 数学が得意な方はピンと来たと思いますが、これはベクトル式です。 ベクトルとベクトルの加減算はベクトル、ベクトルとスカラーの乗除算はベクトルです。 数学的には内積および外積としてベクトルとベクトルの掛け算も存在しますが、言語の基本仕様としてはサポート外です。 別途、数学演算用の関数などを使用してください。


4−6.オブジェクト(構造体)

1次元配列というとベクトルになりますが、ベクトルというと、XだとかYだとかZだとかの成分の名前が欲しくなります。 以下のような記述で、名前つきのベクトルを扱えます。

===============
vec={x:2.4, y:-0.5, z:1.0};
print_string("X成分=" + vec.x + "\n");
print_string("Y成分=" + vec.y + "\n");
print_string("Z成分=" + vec.z + "\n");
===============

変数vecに代入されたものを『オブジェクト』と呼びます。オブジェクトの中身には「.」でアクセスできます。これを『ドット演算子』と呼びますが、単に『点』と呼ぶことも多いです。 C言語をご存知の方は、vec.xという記述をみてクラスや構造体を思い出したと思いますが、考え方はほぼ同じです。(structによる構造体宣言はありません) オブジェクトは配列と同様に、加減乗除が可能です。

===============
vec1={x:2.4, y:-0.5, z:1.0};
vec2={x:-1.1, y:0.3, z:2.9};
print_string("ベクトルの加算=" + (vec1 + vec2) + "\n");
print_string("ベクトルの減算=" + (vec1 - vec2) + "\n");
print_string("ベクトルの乗算=" + (vec1 * 2) + "\n");
print_string("ベクトルの除算=" + (vec1 / 2) + "\n");
===============

前述の配列及びオブジェクトは、いずれも値であるという点では変わりません。 そのため、両方を組み合わせて記述することはもちろん可能です。

===============
combi1=[{x:0.0 y:0.0, z:0.0}, {x:100.0 y:100.0, z:100.0}, -1];
combi2={pos_top:[2,4], pos_bottom:[-2,-4], vec:{x:4, y:1, z:2}};
===============

上記のようにオブジェクトの中にオブジェクト、配列の中にオブジェクト、 オブジェクトの中に配列、というように自由に組み合わせる事が可能です。


最初のページへ戻る