Lisp 如何使用SETF函数来扩展SETF?

Lisp 如何使用SETF函数来扩展SETF?,lisp,common-lisp,setf,Lisp,Common Lisp,Setf,在实用的公共Lisp章节部分访问器函数中,我发现很难理解SETF的扩展方式 职能: (defun (setf customer-name) (name account) (setf (slot-value account 'customer-name) name)) 银行账户类别定义: (defclass bank-account () ((customer-name :initarg :customer-name :initform (error "Must supp

在实用的公共Lisp章节部分访问器函数中,我发现很难理解
SETF
的扩展方式

职能:

(defun (setf customer-name) (name account)
  (setf (slot-value account 'customer-name) name))
银行账户
类别定义:

(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)
    返回类
    银行帐户的一个SETFable slot值
    customer name
    ,然后
    setf
    使用该值将该值设置为“Sally Sue”

  • (setf(customer name my account)“Sally Sue”)
    是否实际调用了上述函数

  • 如上所述,
    setf customer name
    a函数

  • 在上面的函数中,
    (setf customer name)
    中的
    客户名称
    ,以及主体中的
    客户名称
    ,指的是同一事物吗

  • 该节指出

    第二个元素是符号,通常是用于访问SETF函数将设置的位置的函数的名称

    如果是这种情况,那么当函数可用于访问位置时,为什么要在函数定义中使用
    插槽值
    函数呢


在许多情况下,访问和设置数据需要两件事:

  • 从数据结构中检索内容的一种方法
  • 在数据结构中设置某些内容的方法
因此,可以定义setter函数和getter函数。对于简单的情况,它们也可能看起来很简单。但对于复杂的情况,它们可能不会。现在,如果您知道getter的名称,setter的名称是什么?或者:如果您知道setter的名称,那么getter的名称是什么

CommonLisp的思想是,您只需要知道getter的名称

  • getter被调用,比如说,
    GET-FOO

  • 然后调用setter函数
    (SETF GET-FOO)
    。总是

  • setter函数可以这样调用:
    (setf(getfoosomebar)newfoo)
    。总是

因此,您编写了
GET-FOO
函数。您还编写了
(SETF GET-FOO)
函数,Common Lisp将其注册为setter函数

(SETF GET-FOO)
是一个列表。它也是一个函数的名称。这里我们有一个例外:CommonLisp有时允许将列表作为函数名。因此,并非所有函数名都是符号,有些实际上是列表

(setf(customer name my account)“Sally Sue”)
实际上是对已定义setter的调用
my account
是一个变量,其值将绑定到setter的
account
变量
“Sally Sue”
是一个字符串,它将绑定到setter的
name
变量

作为一名开发人员,您只需了解getter:

  • getter的使用:
    (客户名称我的帐户)

  • setter的使用:
    (setf(客户名称我的帐户)“Sally Sue”)
    SETF
    是一个宏,它扩展为对setter函数的调用

上面定义了一个名为
(setf customer name)
的setter函数

CL-USER 80>(功能(setf客户名称))
#

当函数通过
SETF
宏调用时,它会调用另一个setter——这次是通过插槽名称访问插槽值。

SETF
是一个非常复杂的宏,它知道如何将其第一个参数(通常看起来像函数调用)解码为“位置”然后调用将位置设置为新值所需的任何形式。将
(客户名称我的帐户)
视为在
setf
表达式中返回任何内容是没有用的。
setf
宏将在中定义的规则应用于其位置表单,并且作为默认情况,将转换

(setf (foo arg0 arg1 ...) new-val)


实用公共Lisp中的这段话以一种略带椭圆的方式解释了当您在
defclass
插槽定义中指定
:accessor
选项时,在幕后会发生什么。

So
(setf(customer name my account)“Sally Sue”)
调用
(setf customer name)
?但该函数需要两个参数,并且只传递一个参数,
“Sally Sue”
。@BleedingFingers,
(setf customer name)
是您的setter函数。它的名称不仅仅是一个符号,而是一个列表,所以它并不明显,但是当您键入
(setf(customer name my account)“Sally Sue”)
时,您调用setter并传递两个参数:string“Sally Sue”和
bank account
类的对象。这个特定行为是在哪里定义的?如果
帐户
(setf customer name)
的第一个参数,而
帐户
的第二个参数,那么其工作原理是否相同?(看起来有点像黑魔法。)
setf
函数的语义在中定义。新值始终作为第一个参数传递给
setf
函数。感谢您的回答。
CL-USER 80 > (function (setf customer-name))
#<interpreted function (SETF CUSTOMER-NAME) 40A00213FC>
(setf (foo arg0 arg1 ...) new-val)
(funcall #'(setf foo) new-val arg0 arg1 ...)