MikoScript3 言語仕様
参照(リファレンス)
「参照(リファレンス)」は、対象の箱を指し示すもので、あくまで、対象の箱自身で
はありませんが、対象の箱への参照によるアクセスは、対象の箱の別名(エイリアス)で
あるかのように表記できます。そういう意味で、本言語の「参照」は、C++言語の「リ
ファレンス」と似ていますが、本質的な相違もあります。
●参照の設定
参照は、箱に格納できます。たとえば、箱 X への参照を、箱 R に格納するには、以下
のように、参照代入演算子 := を使います。
R := X;
参照が入っている箱を「参照箱」と呼びます。上例では、箱 R が、参照箱になります。
参照代入演算子 := に関しては、「演算子」の章で説明しています。
また、参照の設定は、リレー型関数 'ref が返す参照を代入することによってもできま
す。これに関しては、本章で後述します。
●参照先へのアクセス
参照箱へアクセスすると、その実質の対象は、その参照箱自身ではなく、その参照箱の
参照先の箱になります。たとえば、
A = "This is A.";
R := A; // 箱 R に、箱 A への参照を設定
print R; // プリントの対象は、箱 R の参照先の箱 A になる
を実行すると、「This is A.」とプリントされます。また、このあとに、
A = 1;
R++; // ++ の対象は、箱 R の参照先の箱 A になる
を実行すると、箱 A の中身が 1 増えて、2 になります。このとき、参照箱 R の中身は
変わりません。
このように、参照箱は、実体の箱の別名(エイリアス)としての使い方ができます。
●参照箱自身へのアクセス
参照箱自身がアクセスの対象になるのは、上述の参照代入演算子 := の左辺(代入先)
になる場合です。たとえば、
A = "This is A.";
B = "This is B.";
P := A;
を実行すると、箱 P に、箱 A への参照が設定されますが、このあとに、
P := B;
を実行すると、箱 P の参照先の箱 A に、箱 B の中身が代入されるのではなく、箱 P 自
身に、箱 B への参照が代入されます。つまり、参照箱 P の参照先が、箱 A から箱 B に
変わります。ちなみに、上例で、参照代入演算子 := の代わりに、標準代入演算子 = を
使って
P = B;
を実行すると、箱 P の参照先の箱 A に箱 B の中身が代入されます。
なお、参照箱自身がアクセスの対象になるのは、この他にも、リレー型関数 'ref? の
場合がありますが、これに関しては、後述します。
●参照箱への参照
参照箱への参照は、その参照箱の参照先の箱への参照になります。例えば、
X = "This is X.";
P1 := X;
P2 := P1;
P3 := P2;
を実行すると、箱 P1, 箱 P2, 箱 P3 はともに、箱 X への参照になります。
つまり、参照箱への参照を、箱に格納することはできません。
なお、参照箱以外なら、どの種類の箱への参照も、箱に格納できます。
●参照先と参照箱の削除
参照先の箱が削除されると、その箱を参照しているどの箱も自動的に削除されます。つ
まり、参照先の実体がない参照箱は、自動的に削除されます。たとえば、
X = "This is X.";
P1 := X;
P2 := X;
を実行すると、箱 P1 と箱 P2 に、箱 X への参照が設定されます。次に、
delete X;
を実行すると、箱 X が削除されますが、それに伴って、箱 X を参照している箱 P1 と箱
P2 も自動的に削除されます。また、
X = "This is X.";
P1 := X;
P2 := X;
の次に、
delete P1;
を実行しても、delete の対象は、参照箱 P1 の参照先の箱 X になるので、箱 X の削除
に伴って、その箱を参照している箱 P1 と箱 P2 も自動的に削除されます。
参照先の箱は存続させて、参照箱だけを削除するには、参照箱を一旦、空箱などの参照
箱以外の箱にしてから、削除します。たとえば、
X = "This is X.";
P := X;
で、箱 P に、箱 X への参照が設定されているときに、箱 X は存続させておいて、箱 P
だけを削除するには、
P := null;
として、箱 P 自身を一旦、空箱にします。このとき、箱 P は、もはや参照箱ではなく
なっています。この状態で、
delete P;
を実行すると、箱 P だけが delete されることになります。
●参照によるスコープの規定
複合箱(「箱を入れる箱」)への参照は、スコープを規定できます。たとえば、
A.B.C
は、箱 A の中の箱 B の中の箱 C を示しますが、
P := A.B;
として、箱 P に、箱 A の中の箱 B への参照を設定しておけば、
P::C
P.C
はどちらも、箱 A の中の箱 B の中の箱 C を示します。
ここで、. と :: はスコープを規定する二項演算子です。この演算子は、「演算子」の
章で説明しています。また、スコープに関しては、「スコープ」の章で説明しています。
●箱の中の参照箱
箱の中に参照箱を入れることもできます。たとえば、
A = "This is A.";
X.P := A;
を実行すると、箱 X の中の箱 P に、箱 A への参照が設定されます。
●参照による関数コール
関数への参照を参照箱に設定しておくと、その参照箱を使って、対象の関数をコール
することができます。たとえば、
function fx()
{
print "Function fx() called.";
}
という関数が定義されているときに、
fp := fx;
を実行すると、箱 fp には、関数 fx への参照が設定されます。次に、
fp();
を実行すると、参照箱 fp の参照先の関数 fx がコールされて、
Function fx() called.
とプリントされます。
なお、関数の実体は複製されないので、標準代入で、
fp = fx;
としても、箱 fp に、関数 fx への参照が設定されます。しかし、この弊害については、
「関数の代入と参照によるコール」で説明します。
●関数の引数
関数をコールする時に、その引数を箱名単体にした場合、その箱の中身が渡されるので
はなく、その箱への参照が渡されます。そのため、関数側でその引数に標準代入を行なう
と、実質の代入先は、その引数の参照先の箱になります。これを認識しておかないと、不
本意な結果を招くことになります。例えば、
function f1( x )
{
x++;
}
という関数が定義されているとして、
A = 2;
f1( A );
を実行すると、関数 f1 がコールされた時に、引数 x に箱 A への参照が設定されるので、
演算子 ++ の実質の対象は箱 A になり、最終的に、箱 A の値が 1 増えて 3 になります。
関数の引数の箱の中身を、参照から、実値に変えたい場合は、その引数自身に、その参
照先の値を代入します。これは、例えば、以下のようにして行ないます。
function f2( x, y, z )
{
x := +x; // 引数 x の中身は、参照先の数値になる
y := y'val; // 引数 y の中身は、参照先の実値になる
z := z + 3; // 引数 z の中身は、参照先の数値に 3 を足した数値になる
・・・・
}
ここで、重要なことは、まず、実値に変えたい引数を、参照代入演算子 := の左辺にして
いることです。これによって、代入の対象が、その引数の参照先の箱ではなく、その引数
自身の箱になります。次に、参照代入演算子 := の右辺の評価値が実値になることです。
なお、'val は、対象の実値を返す関数で、本言語にあらかじめ組み込まれているリレー
型関数です。この関数については別途説明します。
なお、引数が複合箱への参照の場合には、上述の参照から実値への変換は行なえません。
その場合、通常、そのまま使いますが、どうしてもその実体が別途必要なら、その実体の
複製を、別の箱に代入することになります。この操作は、「標準代入」そのものです。
●参照への変換( 'ref )
リレー型関数 'ref は、本言語にあらかじめ組み込まれている関数で、対象の箱への参
照を返します。たとえば、
X = "This is X.";
P = X'ref;
を実行すると、箱 P には、箱 X への参照が代入されます。これは、
P := X;
と同じようですが、若干違います。それは、箱 P が参照箱の場合です。この場合、
P = X'ref;
では、箱 P の参照先の箱に、箱 X への参照が代入されます。
関数 'ref を使うと、参照を関数の返値にできます。たとえば、
function f()
{
return ::A'ref;
}
と定義された関数は、グローバルスコープ内の箱 A への参照を返します。この参照を返
す関数 f() を使った例を、以下に示します。
P := f();
を実行すると、箱 P 自身にグローバルスコープ内の箱 A への参照が設定されます。
::A = 1;
f()++;
を実行すると、グローバルスコープ内の箱 A の中身の整数値は 1 増えて 2 になります。
f() = 3;
を実行すると、グローバルスコープ内の箱 A の中身は、整数値 3 になります。
なお、リレー型関数 'ref の対象が箱でない場合、その返値は、null になります。
例えば、123'ref は、null になります。
●参照かどうかの確認( 'ref? )
リレー型関数 'ref? は、本言語にあらかじめ組み込まれている関数で、その対象が、
参照箱か否かを確認する時に使います。この関数は、その対象が、参照箱の場合は 1 を
返し、参照箱でない場合は 0 を返します。たとえば、
X = "This is X.";
P := X;
を実行すると、箱 P には、箱 X への参照が代入されますが、ここで、
P'ref?
の値は、1 になります。一方、
X'ref?
の値は、0 になります。また、
123'ref?, "ABC"'ref?, null'ref?
等の値も、0 になります。
通常、参照箱へアクセスすると、実際の対象は、その参照先の箱になりますが、この
'ref? 関数と、:= の参照代入演算子 の左辺(代入先)に限っては、参照箱へアクセス
しても、その対象は、その参照箱自身になります。
●逆参照アクセス( 'alias )
リレー型関数 'alias は、本言語にあらかじめ組み込まれている関数で、その対象の
箱への参照箱へアクセスする時に使います。たとえば、前節と同様に、
X = "This is X.";
P := X;
を実行すると、箱 P には、箱 X への参照が代入されますが、ここで、
X'alias
は、箱 X への参照箱、つまり、箱 P を示します。これは、箱 P への参照ではありま
せんが、これで、箱 P へのアクセスができます。まず、これが代入先になる場合、
たとえば、
X'alias = 123;
のような場合、X'alias は参照ではないので、代入先は、箱 P 自身になります。これは、
X'alias := 123;
を実行したのと同じです。一方、
print X'alias;
のような場合、X'alias が示す箱 P の中身は参照なので、実質的なアクセス対象は、
その参照先の箱、つまり、箱 X の中身になります。しかし、
print X'alias'name;
のような場合、X'alias が示す箱 P の中身とは無関係に、箱の名前が対象になるので、
プリントされるのは、箱 P 自身の名前、つまり、「P」になります。
対象の箱への参照箱が存在しない場合、または、対象が箱でない場合、'alias は、
null を返します。これを使って、たとえば、
if( X'alias == null )
.....
のようにすれば、箱 X を参照している箱があるかないかを確認できます。
対象の箱への参照箱が複数ある場合、
X'alias( i )
のようにして、指定番目の参照箱にアクセスできます。ここで、i = 0,1,2, ... は、
第1,2,3,... 番目に、箱 X を参照している参照箱の指定になります。指定番目の
参照箱がなければ、この返値は、null になります。