我目前正在阅读 Peter Seibel 的 Practical Common Lisp 中关于类的章节,我对 accessor functions 的使用感到困惑。
设置
我不明白下面给出的涉及银行账户和客户姓名的示例的 setf
函数的新定义:
(defun (setf customer-name) (name account)
(setf (slot-value account 'customer-name) name))
它的用法如下:
(setf (customer-name my-account) "Sally Sue")
为什么
setf
的定义有两个参数 (name account)
,但我们提供的不是这个?上面的 customer-name
函数与后面定义的 customer-name
读取器函数有什么关系(见下文)?(defgeneric (setf customer-name) (value account))
(defmethod (setf customer-name) (value (account bank-account))
(setf (slot-value account 'customer-name) value))
直接插槽访问
访问器函数的动机是避免直接访问槽;接口(interface)高于实现等等。但是 Common Lisp 提供了
:reader
、 :writer
和 :accessor
槽选项来做到这一点。例如。(customer-name
:initarg :customer-name
:initform (error "Must supply a customer name.")
:accessor customer-name)
我是否正确理解这应该仅用于可以直接访问的插槽?因为如果我们稍后决定不应该直接访问插槽,我们就会破坏事情。
最佳答案
setf
有点神奇。 setf
的要点是使设置值的语法类似于访问该值的语法。对于使用 setf
(或 defun
)定义 defmethod
函数的情况, (setf (f ...) val)
变得等效于 (funcall #'(setf f) val ...)
。这就是为什么 setf
在你的例子中只接受一个参数的原因;传递给 (setf customer-name)
的第二个参数是 my-account
。如果你想阅读更多关于 setf
内部的信息,我写了一篇关于它的博客文章,你可以找到 here 。
由于将读取器和写入器写入插槽非常常见,因此 defclass
提供了 :reader
、 :writer
和 :accessor
选项。当您传入这些选项之一时,defclass
将自动编写相应的读取器和写入器。例如,插槽定义:
(customer-name
:accessor customer-name
...)
会自动写代码:
(defgeneric customer-name (account))
(defmethod customer-name ((account bank-account))
(slot-value account 'customer-name))
(defgeneric (setf customer-name) (value account))
(defmethod (setf customer-name) (value (account bank-account))
(setf (slot-value account 'customer-name) value))
为你!
至于你关于直接访问插槽的问题,我见过的最常见的模式是为所有插槽制作访问器,然后只导出应该可以直接访问的插槽的访问器。这样,您可以直接从定义类的同一个包中访问所有插槽,但从不同的包中您只能访问“导出”的插槽。
关于class - Common Lisp 类槽的访问器函数,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34713092/