Riceにおける変数
変数
メモリ領域に存在する、あるクラスの状態を表す一群のデータをインスタンスと呼ぶ。インスタンスのメモリ上での位置を指定するデータをアドレスと呼ぶ。
Riceにおける変数とはインスタンスのアドレスを保持するメモリ領域に付けられた名前である。つまり、参照である。
型
型とは、インスタンスのデータに対する制限と振舞の指定である。
宣言と定義と代入
変数は宣言により名前の導入と型の拘束が行われ定義により初期設定され名前とアドレスの対応が代入により変更される。
変数の宣言、定義、代入は、フィッタ、セッタ、ゲッタ、メソッド定義内の任意の場所で行える。クラス定義内のそれ以外の場所で行われる変数の宣言はフィールドの宣言である。フィールドは宣言と定義を同時に行うことはできない。
クラス定義の外で変数の宣言、定義、代入を行うことは出来ない。つまり、大域変数はRiceには存在しない。
宣言
型名に名前が続いた文は宣言文である。
例えば
int i;
これはint型の i という名前の変数の宣言である。これ以降、この変数はint型のインスタンスのアドレスしか保持出来ない。
ほとんどの場合、宣言文における型名は単純な型名の指定である。
例えば
int i;
string s;
ただし、コレクション型のlist,dictionary,queue,stackは内部に保持するタイプ名を指定することができる。
例えば
list{int} listInt;
stack{string} stackString;
上記のように宣言されたコレクション型の変数が指定されたタイプ以外の要素を保持することはできない。
内部に保持する要素の型は、任意の数を入れ子にすることができる。
例えば
list{dictionary{long}} ldl;
stack{queue{list{bool}}} sqlb;
定義
宣言に代入記号と式が続いた文は定義文である。代入記号の右辺の式は適切な型のインスタンスを返す必要がある。
例えば
int i = 10; // intリテラルはint型のインスタンスを返す。
上記はint型の変数の定義である。
int型は仕様により宣言で自動的に初期化される。ある型の変数に定義が必要かどうかは型により異なる。
範囲の大きな数値クラスを範囲の小さな数値クラスで定義できる。
real rr = 10L; // realをlongで定義。
real rrr = 10; // realをintで定義。
long ll = 10; // longをintで定義。
この場合、右辺を基に左辺のクラスが新たに生成されて定義される。
代入
変数への代入は、変数の参照するアドレスの変更である。
例えば
i = 10; // 変数 i の参照をintリテラルの返すインスタンスのアドレスに変更した。
j = i; // 変数 j の参照を i の参照に変更した。すなわち、i と j は同じインスタンスを参照している。
範囲の大きな数値クラスへ範囲の小さな数値クラスを代入できる。
real rr;
rr = 10L; // realへlongを代入。
rr = 10; // realへintを代入。
long ll;
ll = 10; // longへintを代入。
この場合、右辺が返すインスタンスを基に左辺値のクラスのインスタンスが新たに生成されて、それが代入される。
スコープ
スコープとは、ある変数を使用可能な、ソースコードの範囲である。
クラス定義、フィッタ定義、セッタ定義、ゲッタ定義、メソッド定義は、それらに囲まれるコードにスコープを生成する。if文、while文、fromto文、keepon文、each文、try文も同様である。
スコープが他のスコープを囲む場合、内側のスコープは外側のスコープを見ることができるが、外側のスコープは内側のスコープの内部にある変数を見ることはできない。
例えば
class sample
bool _flag;
open method void sample_method()
bool method_flag = _flag; //♦1
if(true)
bool if_flag = method_flag; //♦2
endif
bool method_flag2 = if_flag; //♦3 エラー。
endmethod
endclass
♦1: _falgはクラス定義の作成したスコープで宣言。内側から見える。
♦2 : method_flagはメソッド定義の作成したスコープで宣言。内側から見える。
♦3 : if_flagはif文の作成したスコープで宣言。外側から見えない。
隠蔽
スコープが他のスコープを囲んで同じ名前の変数が両方のスコープで宣言されている場合、内部で宣言された変数が内部スコープで優先的に使用される。すなわち、内部で宣言された変数が外部の同一名の変数を隠蔽する。
例えば
class sample
open method void sample_method()
bool flag = true;
if(true)
bool flag = false; //♦1
bool flag2 = flag; //♦2
endif
endmethod
endclass
♦1 : 同じ名前の変数を定義。
♦2 : if文内のflagが参照される。flag2の値はfalse。
ただし、内部スコープで同一名の変数を宣言するまでは外部の変数が参照可能である。
class sample
open method void sample_method()
bool flag = true;
if(true)
bool flag2 = flag; //♦1
bool flag = false; //♦2
flag2 = flag; //♦3
endif
endmethod
endclass
♦1 : 外部のflagが参照される。flag2の値はtrue。
♦2 : ここから隠蔽される。
♦3 : if文内のflagが参照される。flag2の値はfalse。
更に、変数の定義文は右辺から評価されるので。
class sample
open method void sample_method()
bool flag = true;
if(true)
bool flag = flag; //♦1
endif
endmethod
endclass
♦1 : 定義文は右辺から評価される。内部のflagが外部のflagで初期化された。これ以降、隠蔽される。
変数が隠蔽された場合、隠蔽された変数を参照するための特別な記法は無い。必要に応じて隠蔽を避けるのはプログラマの責任である。
他の隠蔽については、”識別子の隠蔽”を参照すること。