如何使用SETF函数扩展SETF工作?

时间:2014-06-06 21:03:05

标签: lisp common-lisp setf

Practical Common Lisp 章节17. Object Reorientation: Classes部分访问者函数中,我发现很难理解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)返回类customer-name的SETFable广告位值bank-account,然后SETF用来设置“Sally Sue”的价值?

  • 实际上是(setf (customer-name my-account) "Sally Sue")调用上面的函数吗?

  • 如上定义的是setf customer-name函数?

  • 上面函数中的
  • customer-name中的(setf customer-name)和身体中引用相同内容的'customer-name

  • 部分陈述

      

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

    如果是这种情况那么为什么当函数可以用来访问这个地方时,在函数定义中使用slot-value函数呢?

2 个答案:

答案 0 :(得分:8)

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

  • 从数据结构中检索内容的方法
  • 一种在数据结构中设置内容的方法

因此可以定义一个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)是一个列表。它也是一个功能的名称。这里我们有一个例外:Common Lisp有时允许列表作为函数名。因此,并非所有函数名都是符号,有些实际上是列表。

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

作为开发人员,您只需了解吸气剂:

  • 使用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 - 这次使用通过插槽名称访问插槽值。

答案 1 :(得分:1)

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

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

(funcall #'(setf foo) new-val arg0 arg1 ...)

Practical Common Lisp 中的段落以略微椭圆的方式解释了当您在:accessor槽定义中指定defclass选项时幕后发生的事情。

相关问题