it-roy-ru.com

Разница между `set`,` setq` и `setf` в Common Lisp?

В чем разница между "set", "setq" и "setf" в Common Lisp?

150
Richard Hoskins

Первоначально в LISP не было лексических переменных - только динамические. И не было никакого SETQ или SETF, только функция SET.

Что сейчас написано как:

(setf (symbol-value '*foo*) 42)

был написан как:

(set (quote *foo*) 42)

который в конечном итоге был сокращен до SETQ (SET Quoted):

(setq *foo* 42)

Затем произошли лексические переменные, и SETQ стал использоваться для их присвоения - так что это больше не было простой оберткой вокруг SET.

Позже кто-то изобрел SETF (SET Field) как общий способ присвоения значений структурам данных, чтобы отразить l-значения других языков:

x.car := 42;

будет написано как

(setf (car x) 42)

Для симметрии и общности, SETF также предоставил функциональность SETQ. В этот момент было бы правильно сказать, что SETQ - это низкоуровневый примитив, а SETF - высокоуровневая операция.

Затем произошли макросы символов. Чтобы эти символьные макросы могли работать прозрачно, было понятно, что SETQ должен будет действовать как SETF, если назначаемая "переменная" действительно является символьным макросом:

(defvar *hidden* (cons 42 42))
(define-symbol-macro foo (car *hidden*))

foo => 42

(setq foo 13)

foo => 13

*hidden* => (13 . 42)

Итак, мы прибываем в сегодняшний день: SET и SETQ являются атрофированными остатками старых диалектов и, вероятно, будут загружены из возможных преемников Common LISP.

156
stack programmer
(set ls '(1 2 3 4)) => Error - ls has no value

(set 'ls '(1 2 3 4)) => OK

(setq ls '(1 2 3 4)) => OK - make ls to (quote ls) and then have the usual set

(setf ls '(1 2 3 4)) => OK - same as setq so far BUT

(setf (car ls) 10) => Makes ls '(10 2 3 4) - not duplicated by setq/set
129
Sourav

setq похож на set с первым аргументом в кавычках - (set 'foo '(bar baz)) похож на (setq foo '(bar baz)). setf, с другой стороны, действительно тонко - это похоже на "косвенность". Я предлагаю http://www.nano.com/LISP/cmucl-tutorials/LISP-tutorial-16.html как лучший способ начать понимать это, чем любой ответ здесь может дать ... короче говоря, setf принимает первый аргумент как "ссылку", так что, например, (aref myarray 3) будет работать (как первый аргумент setf), чтобы установить элемент внутри массива.

21
Alex Martelli

Вы можете использовать setf вместо set или setq, но не наоборот, так как setf также может устанавливать значение отдельных элементов переменной, если переменная имеет отдельные элементы. Смотрите примеры ниже:

Все четыре примера назначат список (1, 2, 3) переменной с именем foo.

(set (quote foo) (list 1 2 3))    ;foo => (1 2 3)
(1 2 3)

(set 'foo '(1 2 3))   ;foo => (1 2 3) same function, simpler expression
(1 2 3)

(setq foo '(1 2 3))   ;foo => (1 2 3) similar function, different syntax
(1 2 3)

(setf foo '(1 2 3))   ;foo => (1 2 3) more capable function
(1 2 3)

setf имеет добавленную возможность установки элемента списка в foo на новое значение.

foo                   ;foo => (1 2 3) as defined above
(1 2 3)

(car foo)             ;the first item in foo is 1
1

(setf (car foo) 4)    ;set or setq will fail since (car foo) is not a symbol
4

foo                   ;the fist item in foo was set to 4 by setf
(4 2 3)

Однако вы можете определить макрос символа, который представляет один элемент в foo

(define-symbol-macro foo-car (car foo))    ; assumes FOO => (1 2 3)
FOO-CAR

foo-car               ;foo-car is now a symbol for the 1st item in foo
1

(setq foo-car 4)      ;set or setq can set the symbol foo-car 
4

foo                   ;LISP macros are so cool
(4 2 3)

Вы можете использовать defvar, если вы еще не определили переменную и не хотите задавать ей значение до тех пор, пока в вашем коде не будет.

(defvar foo2)
(define-symbol-macro foo-car (car foo2))
16
dansalmo

Можно думать, что SET и SETQ являются низкоуровневыми конструкциями.

  • SET может установить значение символов.

  • SETQ может установить значение переменных.

Тогда SETF - это макрос, который предоставляет множество видов настроек: символы, переменные, элементы массива, слоты экземпляров, ...

Для символов и переменных можно думать, как будто SETF расширяется до SET и SETQ.

* (macroexpand '(setf (symbol-value 'a) 10))

(SET 'A 10)


* (macroexpand '(setf a 10))         

(SETQ A 10)

Таким образом, SET и SETQ используются для реализации некоторых функциональных возможностей SETF, который является более общей конструкцией. Некоторые из других ответов рассказывают вам немного более сложную историю, когда мы принимаем во внимание символьные макросы.

11
Rainer Joswig

Я хотел бы добавить к предыдущим ответам, что setf - это макрос, который вызывает конкретную функцию в зависимости от того, что было передано в качестве первого аргумента. Сравните результаты макроразложения setf с различными типами аргументов:

(macroexpand '(setf a 1))

(macroexpand '(setf (car (list 3 2 1)) 1))

(macroexpand '(setf (aref #(3 2 1) 0) 1))

Для некоторых типов аргументов будет вызываться функция setf:

(defstruct strct field)
(macroexpand '(setf (strct-field (make-strct)) 1))
4
Filipp