2012年8月22日水曜日

[ドリトル] プロパティや命令の探索

以前、self の記事でも調べたがもう少し調べておこう。 オブジェクトのメソッド(命令)の中でプロパティを参照する際には以下の順序で探索される。
  • 起点指定なし
    ローカル変数 → 自オブジェクトのプロパティ → プロトタイプのプロパティ → ・・・
    → ルートオブジェクトのプロパティ
  • self 起点
    自オブジェクトのプロパティ → プロトタイプのプロパティ → ・・・
    → ルートオブジェクトのプロパティ
  • 特定オブジェクト 起点
    指定オブジェクトのプロパティ → プロトタイプのプロパティ → ・・・→
    ルートオブジェクトのプロパティ

t1 = Turtle ! create.
t1:value = "T1".

t1:test = [ | ; value |
  value = "local".
  TextField ! (value) create.
  TextField ! (self:value) create. ].
t1 ! test.

local T1

「value」で指定した場合はローカル変数の値、「self!value」で指定した場合には t1 オブジェクトのプロパティ値が参照されている。


変数/プロパティ値が undef の場合


上のコードで 「value = "local".」を除去すると「value」の取得値は undef になる。

t1 = Turtle ! create.
t1:value = "T1".

t1:test = [ | ; value |
  TextField ! ("(" + (value) + ")") create.
  TextField ! ("(" + (self:value) + ")") create. ].
t1 ! test.

([undef]) (T1)

ローカル変数の定義自体もなくなれば、「value」の値は t1 のプロパティを参照することになる。

...
t1:test = [ 
...

(T1) (T1)

つまり、値が undef であっても 変数が見つかればその時点で上位のスコープへの探索は停止する。
プロパティも同様の動きになる。

t1 = Turtle ! create.
:value = "T1".
t1:test = [ 
  TextField ! ("(" + (self:value) + ")") create. ].
t1 ! test.

(T1)

t1 = Turtle ! create.
:value = "T1".
t1:value = undef.
t1:test = [ 
  TextField ! ("(" + (self:value) + ")") create. ].
t1 ! test.

([undef])

t1 に プロパティ value が定義されていなければルートの value 値が参照されるが、 t1:value に undef が設定されていると t1:value が採用される。


メソッドの場合


メソッドの探索もプロパティを同じ探索ルールになる。

t1 = Turtle ! create.
t1:test = [ TextField ! "T1" create ].
t2 = t1 ! create.
t2 ! test.

T1

t2 に メソッド test が定義されていないので、プロトタイプである t1 の test メソッドが実行される。

t1 = Turtle ! create.
t1:test = [ TextField ! "T1" create ].
t2 = t1 ! create.
t2:test = undef.
t2 ! test.

エラーがあります。"test" という命令が見つかりません。

今度は t2 に test が定義されていて値が undef なので命令が見つからないというエラーになっている。
メソッド探索もプロパティ値と同様に値が undef でも定義があった時点で探索が停止する。


メソッドの上書きと上位メソッドの呼び出し


Turtle の forward など既定のメソッドを上書きする際、上書きしたメソッド内からプロトタイプの メソッドを呼び出すために同名のローカル変数を定義するのが常套手段になっている。

t1 = Turtle ! create.
t1:forward = [ | n ; forward|
    ! 10 circle.
    ! (n) forward.
 ].
t1 ! 100 forward.

上記のコードで、forward 動作を上書きして「円を描いてから既定の forward 動作をする。」という動きになる。

この動作の意味を考えてみる。
これまでのパターンだとローカルに forward の定義があるので、 t1:forward 定義中に forward 命令を実行しようとした段階で forward 命令がない、ということでエラーになるはず。

同じことを独自メソッドでやってみよう。

t1 = Turtle ! create.
t1:test = [ TextField ! "T1" create ].

t2 = t1 ! create.
t2:test = [ | ; test |
   ! test.
   TextField ! "T2" create ].
t2 ! test.

forward の時と同じだとすれば、t2:test の定義中の !test で t1 の test が呼ばれるはず。
しかし結果は、「"test" という命令が見つかりません。」というエラーになる。

既定のメソッドと独自メソッドでは探索ルールが違うということなのだろうか?
わからない・・・



0 件のコメント:

コメントを投稿