オブジェクトのスロットの値をまとめて変更する


Tags: MOP, ライブラリ, closer-mop

オブジェクトのスロットの値は、初期化の際にまとめて設定することが可能です(オブジェクトの初期化を行う)が、生成した後にまとめて変更したい場合もあります。
この様な場合は、with-slots を利用するのが便利です。

(defclass tips-cl ()
  (a b c d e))
(let ((obj (make-instance 'tips-cl)))
  (with-slots (a b c d e) obj
     (setq a 1
           b 2
           c 3
           d 4
           e 5))
  (describe obj))
;->  #<TIPS-CL {100FBD6C63}>
;      [standard-object]
;
;    Slots with :INSTANCE allocation:
;      A  = 1
;      B  = 2
;      C  = 3
;      D  = 4
;      E  = 5
;
;=>  <no values>

個別スロットの名前を指定するというより、オブジェクトのスロット全体を変更したい場合は、スロット名を取得し、slot-value で変更することになるでしょう。(MOPを利用します)

;; Closer to MOPの導入
(ql:quickload :closer-mop)
(let ((obj (make-instance 'tips-cl)))
  (mapc (lambda (s)
          (setf (slot-value obj (c2mop:slot-definition-name s))
                :foo!))
        (c2mop:class-slots (class-of obj)))
  (describe obj))
;->  #<TIPS-CL {100FC71373}>
;      [standard-object]
;
;    Slots with :INSTANCE allocation:
;      A  = :FOO!
;      B  = :FOO!
;      C  = :FOO!
;      D  = :FOO!
;      E  = :FOO!
;
;=>  <no values>

また slot-value を用いる方法よりは多少の制限がありますが、より高速なアクセスが期待される standard-instance-access を利用する方法もあります。

(let ((obj (make-instance 'tips-cl)))
  (dotimes (idx (length (c2mop:class-slots (class-of obj))))
    (setf (c2mop:standard-instance-access obj idx)
          :foo!))
  (describe obj))
;->  #<TIPS-CL {100FC71374}>
;      [standard-object]
;
;    Slots with :INSTANCE allocation:
;      A  = :FOO!
;      B  = :FOO!
;      C  = :FOO!
;      D  = :FOO!
;      E  = :FOO!
;
;=>  <no values>