在 Practical Common Lisp 章节中 17. Object Reorientation: Classes Accessor Functions 部分,我发现很难理解 SETF
的扩展方式。
功能:
(defun (setf customer-name) (name account)
(setf (slot-value account 'customer-name) name))
bank-account
类定义:
(defclass bank-account ()
((customer-name
:initarg :customer-name
:initform (error "Must supply a customer name."))
(balance
:initarg :balance
:initform 0)
(account-number
:initform (incf *account-numbers*))
account-type))
我不明白的地方:
在表达式
(setf (customer-name my-account) "Sally Sue")
中(customer-name my-account)
返回一个 SETFablebank-account
类的 slot-valuecustomer-name
然后SETF
用来将值设置为“Sally Sue”?(setf (customer-name my-account) "Sally Sue")
是否实际调用了上面的函数?上面定义的
setf customer-name
是一个函数吗?在上面的函数中是
(setf customer-name)
中的customer-name
和body中的'customer-name
指的是同一件事吗?章节状态
second element is a symbol, typically the name of a function used to access the place the SETF function will set
如果是这样,那么当函数可用于访问该位置时,为什么还要在函数定义中使用
slot-value
函数?
最佳答案
在很多情况下访问和设置数据,需要做两件事:
- 一种从数据结构中检索内容的方法
- 一种在数据结构中设置内容的方法
因此可以定义一个 setter 函数和一个 getter 函数。对于简单的情况,它们也可能看起来很简单。但对于复杂的情况,他们可能不会。现在如果你知道 getter 的名字,那么 setter 的名字是什么?或者:如果知道 setter 的名称,那么 getter 的名称是什么?
Common Lisp 认为您只需要知道 getter 的名称即可。
getter 被称为
GET-FOO
然后调用 setter 函数
(SETF GET-FOO)
。总是。setter 函数可以这样调用:
(setf (get-foo some-bar) new-foo)
。总是。
因此您编写了 GET-FOO
函数。您还编写了 (SETF GET-FOO)
函数,Common Lisp 将其注册为 setter 函数。
(SETF GET-FOO)
是一个列表。它也是一个函数的名称。这里有一个异常(exception):Common Lisp 有时允许将列表作为函数名。因此,并非所有函数名称都是符号,有些实际上是列表。
(setf (customer-name my-account) "Sally Sue")
实际上是调用定义的setter。 my-account
是一个变量,其值将绑定(bind)到 setter 的 account
变量。 "Sally Sue"
是一个字符串,它将绑定(bind)到 setter 的 name
变量。
作为开发人员,您只需了解 getter:
getter 的使用:
(customer-name my-account)
setter 的使用:
(setf (customer-name my-account) "Sally Sue")
。SETF
是一个宏,它扩展为对 setter 函数的调用。
(defun (setf customer-name) (name account)
(setf (slot-value account 'customer-name) name))
上面定义了一个名为 (setf customer-name)
的 setter 函数。
CL-USER 80 > (function (setf customer-name))
#<interpreted function (SETF CUSTOMER-NAME) 40A00213FC>
当通过 SETF
宏调用该函数时,它会调用另一个 setter - 这次使用通过槽名称访问槽值。
关于lisp - 使用 SETF 函数扩展 SETF 是如何工作的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24090571/