Common Lisp 中 类 的吸气剂和吸气剂
Getters and setters to classes in Common Lisp
我经常有一个 class 由另一个 class 的列表组成。例如,我将有一个由矢量组成的矢量列表 class。为了避免写长语句,我写了一个方法来访问嵌入的class。但是,此方法仅作为 getter;我不能用它来设置插槽值。有没有办法使用方法来设置一个 class 槽值?
下面是一个最小的例子:
(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)`
但是如果你不想暴露你有 arrays/vectors 的事实,你可以定义一个自定义的 setf 扩展器。
首先,在你的class中只将access-vector-name
定义为:reader
。
那么:
(defun (setf access-vector-name) (newval obj index)
(setf (aref (access-vector-name obj) index) newval))
如果意图是隐藏底层实现,也许 access-vector-name
是个坏名字。
您只需定义一个 setter 方法即可。但是,您的代码目前是不合法的: VECTOR
是 CL
包中定义的符号(实际上同时命名了函数和类型)因此定义了一个 class 称为 VECTOR
是非常非法的(一个体面的实施会对此感到厌烦)。这是您的代码的一个版本,其中基本 class 重命名为 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 没有预定义的宏来定义这样的访问器方法,在 class 定义之外:我不确定为什么,但可能是因为它确实是这样的 'pure' 访问器的情况相对不常见。
我经常有一个 class 由另一个 class 的列表组成。例如,我将有一个由矢量组成的矢量列表 class。为了避免写长语句,我写了一个方法来访问嵌入的class。但是,此方法仅作为 getter;我不能用它来设置插槽值。有没有办法使用方法来设置一个 class 槽值?
下面是一个最小的例子:
(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)`
但是如果你不想暴露你有 arrays/vectors 的事实,你可以定义一个自定义的 setf 扩展器。
首先,在你的class中只将access-vector-name
定义为:reader
。
那么:
(defun (setf access-vector-name) (newval obj index)
(setf (aref (access-vector-name obj) index) newval))
如果意图是隐藏底层实现,也许 access-vector-name
是个坏名字。
您只需定义一个 setter 方法即可。但是,您的代码目前是不合法的: VECTOR
是 CL
包中定义的符号(实际上同时命名了函数和类型)因此定义了一个 class 称为 VECTOR
是非常非法的(一个体面的实施会对此感到厌烦)。这是您的代码的一个版本,其中基本 class 重命名为 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 没有预定义的宏来定义这样的访问器方法,在 class 定义之外:我不确定为什么,但可能是因为它确实是这样的 'pure' 访问器的情况相对不常见。