#:g1: 実践SETF定義:defstruct、defclassで定義できるおまけsetf

Posted 2018-12-15 13:52:03 GMT

Lisp SETF Advent Calendar 2018 15日目 》

setf系構文の紹介ですが、今回は、defstruct、defclassでおまけに定義できるsetfの紹介です。

defstructsetf

MACLISP系Lispではsetfdefstructは誕生から共に歩んできた感じですが、defstructの便利機能の一つに、アクセサを自動生成してくれる、というのがあります。

(defstruct zot x y z)

(let ((z (make-zot))) (setf (zot-x z) 42) z) → #S(zot :x 42 :y nil :z nil)

デフォルトで(setf zot-x)という名前で作成してくれますが、この名前が気に入らない場合は、調整することも可能です。

(defstruct (zot (:conc-name ""))
  x y z)

(let ((z (make-zot))) (setf (x z) 42) z) → #S(zot :x 42 :y nil :z nil)

しかし、定義しない、という選択はできないので、ごく稀に名前の競合などで面倒なことがあったりはします。

defclasssetf

後発のdefclassでは、defstructのように決め打ち動作は抑えられるため名前の競合問題は制御可能です。

スロットの指定で、:accessorを指定すれば、リーダーメソッドと(setf 名前)というセッターメソッドが定義されます。

:writerでは、シンボルと(setf fn)形式が指定可能で、スロットに複数指定可能なので複数の名前が一度に定義できます。

(defclass kiji ()
  ((x :accessor kiji-x)
   (y :accessor kiji-y)
   (z :accessor kiji-z :writer set-kiji-z :writer (setf hyper-kiji-z))))

(let ((o (make-instance 'kiji))) (setf (kiji-x o) 42) (kiji-x o)) → 42

(let ((o (make-instance 'kiji))) (setf (hyper-kiji-z o) 43) (kiji-z o)) → 43

(let ((o (make-instance 'kiji))) (set-kiji-z 43 o) (kiji-z o)) → 43

ちなみに、アクセサの名前ごとに別の総称関数なので個々のメソッドコンビネーションを付与できます(コンビネーションはstandardのみ可能)

(defmethod (setf hyper-kiji-z) :before (val (obj kiji))
  (print 'hyper-kiji-z))

(let ((o (make-instance 'kiji))) (setf (kiji-z o) 43) (kiji-z o)) → 43

(let ((o (make-instance 'kiji))) (setf (hyper-kiji-z o) 43) (kiji-z o)) ▻ hyper-kiji-z → 43

まとめ

改めて確認してみるとdefclassが随分多機能だなと感心します。


HTML generated by 3bmd in LispWorks 7.0.0

comments powered by Disqus