空想犬猫記

※当日記では、犬も猫も空想も扱っておりません。(旧・エト記)

etolisp 進捗 (29) 〜 Object Systemの実装 (3):defmethod

defmethod の実装が限定的に終わる。今のところ,メソッドの検索は class ID をキーに,ハッシュを利用した n 分木構造を辿る仕組みにしている。効率的ではないかも知れないが,一番,直感的に思える実装。

Generic Function は,関数の配列と,関数を検索するための辞書からなる。具体的には

struct Argument;
typedef map<size_t, shared_ptr<Argument> > ArgumentChain;
struct Argument
{
  size_t _index;
  shared_ptr<ArgumentChain> _next;
};

みたいなノードを定義して,Generic Method クラスが,メンバとしてArgument(root_node)を持つ。引数の class ID 列が 3 7 4 6 3 だとしたら,

root_node->_next[3]->_next[7]->_next[4]->_next[6]->_next[3]->_index

を検索して,目的のメソッドのインデックスを得る。実際には,エラー処理と継承関係にある class ID も検索して最も specialized されたメソッドの定義を選ぶという操作が入るので,少し複雑(といっても数行)になる。specialized されていない引数は _next[0] (class ID = 0)を使う。辞書と配列に分けたのは,GCで管理できるコンテナが配列とハッシュだけであるという事情による。で,引数が無い generic method は,root_node._index で参照できるという仕組み。本当はハッシュ+リスト構造じゃなくて,引数をエンコードして1つのハッシュにするの方が効率がいいんだろうな。そういう最適化は後々考えるとしよう。

Generic function の実装が終わったので,defclass で自動的に定義されるアクセッサも,関数ではなくメソッドで定義するように変更。これによって,:accessor を使って,同じ名前のアクセッサメソッドを定義してもちゃんと動くようになった。

(defclass left () ((name :accessor name)))
(defclass right () ((name :accessor name)))
(setf l (make-left))
(setf (name l) "left-chan")
(setf r (make-right))
(setf (name r) "right-kun")

うーん。いい感じ。ちゃんと,下のようなのも動いている。

(defclass left () ((member :initform 10)))
(defclass right () ((member :initform 20)))
(defclass middle (left right) ())
(defmethod add-member ((l left) (r right)) (+ (left-member l) (right-member r)))
(add-member (make-left) (make-right)) ; => 30
(add-member (make-middle) (make-middle)) ; => 30
(defmethod add-member ((l left) r) (+ (left-member l)  r))
(add-member (make-middle) 5) ; => 15

今週末 auxiliary method と method combination を実装すれば,赤い本的に面白い&難しいところはかなり終わる気がする。あとは果てしなく長いウィニングランって感じかな(あいかわらずめでたい)。

ANSI Common LISP (Prentice Hall Series in Artificial Intelligence)

ANSI Common LISP (Prentice Hall Series in Artificial Intelligence)

「赤い本」とさんざん言っているのはこの本のこと。表は赤くないんだけど,背表紙と裏表紙が赤いんです(笑)。この本,ボロボロになるまで読んでるけど,いまさら会社の先輩に返せないなぁ。