Регистрация | Войти
Lisp — программируемый язык программирования

Чем 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 и для своих функций. Для этого есть целых три способа:

  • DEFINE-SETF-EXPANDER
  • DEFSETF
  • DEFUN (SETF ...)

Примеры смотрите в Common Lisp HyperSpec.

В принципе, SETQ не нужен. SETF для символа вполне мог бы раскрываться во что-то системозависимое. SETQ добавили в Common Lisp только для совместимости с более старыми лиспами.

Я считаю, что стилистически лучше использовать везде SETF. Почему — напишу как-нибудь попозже :)

@2009-2013 lisper.ru