Как работает функция SETF для расширения SETF?
В практическом Common Lisp, глава 17. Переориентация объектов: раздел классов Функции доступа, мне было трудно понять, как 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)
вернуть значение слота SETFablecustomer-name
класса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 ответа
Во многих случаях для доступа к данным и их настройки необходимы две вещи:
- способ извлечь что-то из структуры данных
- способ установить что-то в структуре данных
Таким образом, можно определить функцию установки и функцию получения. Для простых случаев они также могут выглядеть просто. Но для сложных случаев они не могут. Теперь, если вы знаете имя получателя, как называется этот установщик? Или: если вы знаете имя установщика, как называется получатель?
Common Lisp считает, что вам нужно знать только имя получателя.
геттер называется, скажем,
GET-FOO
тогда вызывается функция установки
(SETF GET-FOO)
, Всегда.функция setter может быть вызвана следующим образом:
(setf (get-foo some-bar) new-foo)
, Всегда.
Так ты пишешь GET-FOO
функция. Вы также пишете (SETF GET-FOO)
функция и Common Lisp регистрирует его как функцию-установщик.
(SETF GET-FOO)
это список. Это также название функции. Здесь у нас есть исключение: Common Lisp иногда допускает список в качестве имени функции. Таким образом, не все имена функций являются символами, некоторые на самом деле являются списками.
(setf (customer-name my-account) "Sally Sue")
на самом деле вызов определенного установщика. my-account
переменная, значение которой будет связано с account
переменная сеттера. "Sally Sue"
является строкой, и она будет связана с name
переменная сеттера.
Как разработчик, вы должны знать только геттер:
использование геттера:
(customer-name my-account)
использование сеттера:
(setf (customer-name my-account) "Sally Sue")
,SETF
это макрос, который расширяется до вызова функции установки.
(defun (setf customer-name) (name account)
(setf (slot-value account 'customer-name) name))
Выше определяется функция setter, называемая (setf customer-name)
,
CL-USER 80 > (function (setf customer-name))
#<interpreted function (SETF CUSTOMER-NAME) 40A00213FC>
Когда функция вызывается через SETF
макрос вызывает другой установщик - на этот раз используя доступ к значению слота через имя слота.
setf
это очень сложный макрос, который знает, как декодировать свой первый аргумент, который обычно выглядит как вызов функции, как "место", а затем вызывает любые формы, необходимые для установки места в новое значение. Это не полезно думать о (customer-name my-account)
возвращая что-либо в setf
выражение. setf
макрос применяет правила, определенные в HyperSpec, к своей форме места и, как случай по умолчанию, преобразует
(setf (foo arg0 arg1 ...) new-val)
в
(funcall #'(setf foo) new-val arg0 arg1 ...)
Отрывок из Практического Common Lisp объясняет, в несколько эллиптическом ключе, что происходит за кулисами, когда вы указываете :accessor
вариант в defclass
определение слота.