通用Lisp中类的getter和setter



我经常有一个由另一个类的列表组成的类。例如,我有一个由向量组成的向量列表类。为了避免编写长语句,我编写了一个方法来访问嵌入类。然而,这个方法只作为getter;我不能用它来设置槽位值。是否有一种方法可以使用方法来设置类槽值?

下面是一个简单的例子:

(defclass vector ()
  ((name :accessor vector-name
         :initarg :name)))
(defclass vector-list ()
  ((vectors :accessor vector-list-vectors
            :initarg :vectors)))
(defun make-vector-list ()
  (make-instance 'vector-list
    :vectors (list
               (make-instance 'vector :name 'v1)
               (make-instance 'vector :name 'v2))))
(defmethod access-vector-name ((vt vector-list) vector-idx)
  (vector-name (nth vector-idx (vector-list-vectors vt))))

;; returns V1
(print (access-vector-name (make-vector-list) 0))
;; Now, trying to set the same slot returns an error
;; How can I set the slot?
(setf (access-vector-name (make-vector-list) 0) 'new); --> error

最简单的方法是:

(setf (aref (access-vector-name ...) index) value)`

但是如果你不想暴露你有数组/向量的事实,你可以定义一个自定义的setf展开器

首先,只将access-vector-name定义为类中的:reader。然后:

(defun (setf access-vector-name) (newval obj index)
  (setf (aref (access-vector-name obj) index) newval))

如果目的是隐藏底层实现,也许access-vector-name是一个坏名字

您只需要定义一个setter方法来做到这一点。然而,你的代码是不合法的,因为它站:VECTORCL包中定义的符号(实际上命名函数和类型),所以定义一个叫做VECTOR的类是可怕的非法(和一个体面的实现会呕吐)。下面是一个将基本类重命名为VEC的代码版本,并使用setter方法。

(defclass vec ()
  ;; Don't call it VECTOR since it's a function in CL
  ((name :accessor vec-name
         :initarg :name)))
(defclass vec-list ()
  ((vecs :accessor vec-list-vecs
         :initarg :vecs)))
(defun make-vec-list ()
  (make-instance 'vec-list
    :vecs (list
           (make-instance 'vec :name 'v1)
           (make-instance 'vec :name 'v2))))
(defmethod access-vec-name ((vt vec-list) vec-idx)
  (vec-name (nth vec-idx (vec-list-vecs vt))))
(defmethod (setf access-vec-name) (new (vt vec-list) vec-idx)
  (setf (vec-name (nth vec-idx (vec-list-vecs vt))) new))

CLOS没有预定义的宏来定义这样的访问方法,在类定义之外:我不确定为什么,但也许是因为它真的是像这样的"纯"访问器的情况相对不常见。

相关内容

  • 没有找到相关文章

最新更新