本当は List と一緒に作りたかったのだが、かなり複雑だったので独立してみる。
nil の特徴
-
List である
- car cdr 共に nil を返す
- Atom である
- Symbol である
- それには nil が 束縛 されている
- "nil" という名前を持っている。
xyzzy では以下は全部 t を返却する。
(listp nil) (null (car nil)) (null (cdr nil)) (atom nil) (symbolp nil) (boundp nil) (null (symbol-value nil))
こうなってくると、 nil は各オブジェクトの特殊な場合を指すような気がしてくる。 うーむ、多重継承が欲しくなってくる。
どう実装するか?
-
ISymbol インターフェイスを用意する。
- string Name { get; }
- SymbolicExpression Value { get; }
- void Bind( SymbolicExpression s );
- void Unbind();
- List インスタンスの car cdr 共に nil の場合を nil とする。
- List で ISymbol を実装する。 nil でない場合は例外を投げ、 nil 場合はそれに沿った処理をする。
- Atom を継承し ISymbol を実装して Symbol クラスを作成する。
- SymbolTable は ISymbol を操作するクラスにする。
- Nil に関する動作は SymbolTable で頑張ることにする。
これにより
- 空リストを nil とすることが出来る。
- List インスタンスの nil に対して List 操作と Symbol 操作が行える
- Symbol インスタンスの nil に対しては List 操作を行えないので工夫する必要がある。
- SymbolTable で頑張れば回避可能かも。
NIL 定数を作る
List の特殊なインスタンスを NIL とするので List クラスの static readonly な変数を用意する。
public class List : SymbolicExpression, ISymbol { public static readonly SymbolicExpression NIL; static List() { List nil = new List(); typeof( List ).InvokeMember( "car", BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Instance, null, nil, new object[]{ nil } ); typeof( List ).InvokeMember( "cdr", BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Instance, null, nil, new object[]{ nil } ); List.NIL = nil; } : }
- 通常 List クラスはコンストラクタで引数なしだと car と cdr に定数 NIL を入れておく。
- この記述中の static コンストラクタ内では 定数 NIL には null が入っている。
- この時に List インスタンスを作ると car cdr には null が入ってることになる。
- とりあえず List インスタンスを作る。
- そこに無理矢理リフレクションを使って car と cdr に自身を代入している。
- List クラスの car と cdr は readonly なので通常の代入は出来ないから。
Generated by wifky 1.0.3.1 with Perl 5.008009