В чем разница между "set", "setq" и "setf" в Common Lisp?
Разница между `set`,` setq` и `setf` в Common Lisp?
Ответ 1
Первоначально в 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.
Ответ 2
(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
Ответ 3
setq
похож на set
с цитированным первым аргументом arg (set 'foo '(bar baz))
, как и у t23. setf
, с другой стороны, действительно тонко - это как "косвенность". Я предлагаю http://www.n-a-n-o.com/lisp/cmucl-tutorials/LISP-tutorial-16.html как лучший способ понять его, чем любой ответ здесь может дать... короче говоря, setf
принимает первый аргумент как "ссылку", так что, например, (aref myarray 3)
будет работать (как первый arg to setf
), чтобы установить элемент внутри массива.
Ответ 4
Вы можете использовать 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))
Ответ 5
Можно думать, что SET
и SETQ
являются низкоуровневыми конструкциями.
-
SET
может установить значение символов. -
SETQ
может установить значение переменных.
Тогда SETF
- это макрос, который предоставляет множество видов настроек: символы, переменные, элементы массива, временные интервалы,...
Для символов и переменных можно думать, что SETF
расширяется на SET
и SETQ
.
* (macroexpand '(setf (symbol-value 'a) 10))
(SET 'A 10)
* (macroexpand '(setf a 10))
(SETQ A 10)
So SET
и SETQ
используются для реализации некоторой функциональности SETF
, которая является более общей конструкцией. Некоторые из других ответов говорят вам немного более сложную историю, когда мы учитываем макросы символов.
Ответ 6
Я хотел бы добавить к предыдущим ответам, что 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))