Чем SETF отличается от SETQ
Автор: Иван Болдырев
Источник: http://lispnik.livejournal.com/130098.html
SETQ — специальный оператор, присваивающий значение переменным, как лексическим, так и динамическим. Этим его функции исчерпываются.
SETF — это макрос. Он позволяет делать присваивания в произвольное место. Переменная — тоже место. Но и (car x), (cddr x) и многое другое — тоже места. Любопытно посмотреть, во что раскрывается этот макрос (GNU CLISP 2.33.2) в разных случаях:
[1]> (macroexpand-1 '(setf x 1))
(SETQ X 1) ;
T
[2]> (macroexpand-1 '(setf (gethash x y) 1))
(LET* ((#:G5038 X) (#:G5039 Y) (#:G5040 1))
(SYSTEM::PUTHASH #:G5038 #:G5039 #:G5040)) ;
T
[3]> (macroexpand-1 '(setf (car x) 1))
(SYSTEM::%RPLACA X 1) ;
T
Как видим, если первый аргумент SETF — символ, то он просто перепоручает всю работу SETQ. Если это что-то более сложное (список), то используются системозависимые функции, выбор которых основан на первом элементе этого списка. Если это GETHASH, то вызывается системозависимая функция SYSTEM::PUTHASH, если это CAR, вызывается такая же SYSTEM::%RPLACA.
Разумеется, можно задавать SETF и для своих функций. Для этого есть целых три способа:Как видим, если первый аргумент SETF — символ, то он просто перепоручает всю работу SETQ. Если это что-то более сложное (список), то используются системозависимые функции, выбор которых основан на первом элементе этого списка. Если это GETHASH, то вызывается системозависимая функция SYSTEM::PUTHASH, если это CAR, вызывается такая же SYSTEM::%RPLACA.
Разумеется, можно задавать SETF и для своих функций. Для этого есть целых три способа:
- DEFINE-SETF-EXPANDER
- DEFSETF
- DEFUN (SETF ...)
Примеры смотрите в Common Lisp HyperSpec.
В принципе, SETQ не нужен. SETF для символа вполне мог бы раскрываться во что-то системозависимое. SETQ добавили в Common Lisp только для совместимости с более старыми лиспами.
Я считаю, что стилистически лучше использовать везде SETF. Почему — напишу как-нибудь попозже :)