Section 2.11 コンテキスト

今回はまず問題からいくね。
えっ、最初から問題?
最近難しい話が多いからあんまり自信ないんだけどなぁ…
あ、それならだいじょぶ。この問題は簡単だから。
さて、このスクリプトを実行すると何て表示されるでしょー?

class Counter
{
    var counter;  // メンバ変数(カウンタ)

    // コンストラクタ:カウンタを 0 に初期化します
    function Counter()
    {
        counter = 0;
    }

    // デストラクタ:何もしません
    function finalize(){}

    // カウンタをインクリメントします
    function increment()
    {
        counter++;
    }

    // カウンタの値を返すプロパティ
    property count
    {
        getter()
        {
            return counter;
        }
    }
}  // Counter クラスの定義はここまで

var counter1 = new Counter();
var closure = counter1.increment;
closure();
System.inform(counter1.count);
invalidate counter1;

これって前回やったクロージャだよね?
closurecounter1increment メソッドへの参照を代入して、 それから closure を使って increment メソッドを呼び出してるね。
うん、そだね。
ってことは…?
えっと、最初にコンストラクタでカウンタが 0 に初期化されて、その後 increment メソッドが呼び出されるから、 ここでカウンタが 1 になって…
うんうん。
inform メソッドで、プロパティを使ってカウンタの値を参照してるから、引数は 1 になるよね。
だから、表示されるのは「1」かな。
うん、正解!
これぐらいだったら一応解るよ。
それじゃ、これはどう?

var counter1 = new Counter();
var counter2 = new Counter();
var closure = counter1.increment;
closure();
System.inform(counter1.count);
System.inform(counter2.count);
invalidate counter2;
invalidate counter1;

今度は2つ Counter クラスのオブジェクトがあるけど…
クロージャになってるのは counter1.increment の方だけだから、 最初は「1」って表示されて、次は「0」だよね。
ん、これも正解。
でもこれって counter2 を作る意味ないよね。
まぁね。
でも、closure を使って counter2increment メソッドを呼び出せるんだ。
えっ、そんなことできるの?
というワケで、今回はコンテキストの話の続き。
あ、そういえばコンテキストをもうちょっと詳しく説明するって言ってたよね。
うん。 で、まず closure のコンテキストなんだけど…
closure のコンテキストって counter1 だよね?
ん、そう。
でも、もし closure のコンテキストを counter2 に変えられれば、 closure を使って counter2increment メソッドを呼び出せるよね。
うん、そだね。
で、そのコンテキストの変更をするのが incontextof 演算子なんだ。
incontextof って… §1.15 とかで辞書配列のメソッドを呼び出す時に使った演算子だよね?
ん、それ。
じゃあ incontextof 演算子を使って counter2increment メソッドを呼び出してみるね。
うん。

incontextof 演算子の使用例>

var counter1 = new Counter();
var counter2 = new Counter();
var closure = counter1.increment;
// ↓コンテキストを変更して、counter2 に対して increment メソッドを呼び出すようにします
(closure incontextof counter2)();
System.inform(counter1.count);
System.inform(counter2.count);
invalidate counter2;
invalidate counter1;

ちょっと実行してみて。
うん。
……あ、今度は最初が「0」で次が「1」になった。
ってことは、ちゃんと counter2 のコンテキストで increment メソッドが実行されたってことだね。
ん、そういうこと。
ちなみに、辞書配列のオブジェクトは元々メソッドを持ってないから、 Dictionary クラスに直接属してるメソッドを使って、例えば…

var dic = %["a" => 1, "b" => 2];
// ↓ dic の内容を "dic.txt" に保存します(saveStruct メソッドを dic に対して実行します)
(Dictionary.saveStruct incontextof dic)("dic.txt");

って書くことで、Dictionary クラスの saveStruct メソッドを dic っていう辞書配列に対して実行するってワケ。
なるほどね〜。
だから、辞書配列の時と同じように、こういう書き方もできるよ。

incontextof 演算子の使用例(その2)>

var counter1 = new Counter();
// ↓コンテキストを指定して、counter1 に対して increment メソッドを呼び出すようにします
(Counter.increment incontextof counter1)();
System.inform(counter1.count);
invalidate counter1;

こんなふうに、辞書配列以外の時でも、(クラス名.メソッド名 incontextof オブジェクト名)(引数); って書いても OK。
じゃあ、このスクリプトを実行すると「1」って表示されるってこと?
うん、そう。
他にもこんな使い方もあるよ。

incontextof 演算子の使用例(その3)>

var counter1 = new Counter();
// ↓式中関数を counter1 のコンテキストで実行できるようにします
var func = function(){increment();} incontextof counter1;
func();
System.inform(counter1.count);
invalidate counter1;

これって、式中関数だっけ?
うん、そう。§1.19 でやったよね。
こうすると、式中関数を counter1 のコンテキストで実行できるようになるんだ。
つまり、『increment();』の部分が『counter1.increment();』に置き換わるって感じかな。
へぇ、こんなこともできるんだ…
incontextof 演算子はコンテキストを変更するよりも、こんなふうにコンテキストを指定するために使われる方が多いんじゃないかな。
ふぅん、そうなんだ。
でも incontextof 演算子も使いこなすのが難しそうだね。
ん〜、確かにちょっとややこしいところもあるかな。
まぁ、これも余裕があったら使ってみて、ってことで。
うん、わかった。
じゃ、今回はここまで。
クラス関係は次回で終わりだよ。
あ、そーなんだ。
うん、それじゃまた次回ね。


前へ | TOP | 次へ