我目前正在阅读 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/

10-14 15:49
查看更多