В чем разница между set, setq и setf в Common Lisp?
В чем разница между `set`,` setq` и `setf` в Common Lisp?
Ответы (6)
Изначально в Лиспе не было лексических переменных - только динамические. И не было ни SETQ, ни SETF, только функция SET.
Что сейчас пишется как:
(setf (symbol-value '*foo*) 42)
был написан как:
(set (quote *foo*) 42)
который в конечном итоге был сокращен до SETQ (SET Quoted):
(setq *foo* 42)
Затем появились лексические переменные, и SETQ стал использоваться для присвоения им тоже - так что это больше не была простой оболочкой вокруг SET.
Позже кто-то изобрел SETF (поле SET) как общий способ присвоения значений структурам данных, чтобы отразить 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.
f на самом деле означает функцию, а не поле (или форму, если на то пошло) , и предоставляет ссылки, поэтому, хотя поле setf for имеет некоторый смысл, похоже, что оно может быть неправильным.
- person Joshua Taylor; 22.05.2014
set - это функция. Таким образом, он не знает окружающей среды. set не видит лексическую переменную. Он может устанавливать только символьное значение своего аргумента. setq больше не указан в кавычках. Об этом свидетельствует тот факт, что setq - это особая форма, а не макрос.
- person KIM Taegyoon; 03.07.2018
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) для установки элемента внутри массива.
Вы можете использовать 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))
Можно думать о 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, которая является более общей конструкцией. Некоторые другие ответы рассказывают вам немного более сложную историю, когда мы принимаем во внимание макросы символов.
Я хотел бы добавить к предыдущим ответам, что 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))
car по существу разыменовывает указатель списка, давая вам ссылку на первый элемент (first делает то же самое). (setf (car ls) 10) может быть записано как *ls = 10; в C.
- person Yay295; 13.11.2016
(setf (car ls) 5) совпадает с l->val = 5; в C. А как насчет (setf (car (car (car ls))) 5), если сказать (setq ls '(((1))) )?
- person reuns; 13.11.2016
(setq ls '(((1)))), (setf (car (car (car ls))) 5) - это неопределенное поведение, потому что значение ls является постоянным (например, изменение строкового литерала в C). После (setq ls (list (list (list 1)))), (setf (car (car (car ls))) 5) работает так же, как ls->val->val->val = 5 в C.
- person kyle; 13.06.2017