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

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


person Richard Hoskins    schedule 15.05.2009    source источник
comment
Ответы на их поведение довольно хорошо описаны в ответах, но принятый ответ имеет, возможно, ошибочную этимологию для f в setf. Ответ на вопрос Что означает f в setf? говорит, что это функция, и предоставляет ссылки для ее резервного копирования.   -  person Joshua Taylor    schedule 22.05.2014


Ответы (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.

person stack programmer    schedule 15.05.2009
comment
В Common Lisp всегда были лексические переменные. Вы, должно быть, говорили о каком-то Лиспе до Common Lisp. - person Rainer Joswig; 15.05.2009
comment
Если SET и SETQ должны быть загружены из преемника Common Lisp, им потребуется некоторая замена. Их использование в высокоуровневом коде ограничено, но низкоуровневый код (например, код, в котором реализован SETF) нуждается в них. - person Svante; 16.05.2009
comment
есть ли причина, по которой вы выбрали «автомобиль» в качестве поля вместо того, что может быть запутано как функция автомобиля? - person drudru; 11.07.2010
comment
Этот ответ на Что означает f in setf stand for? утверждает, что f на самом деле означает функцию, а не поле (или форму, если на то пошло) , и предоставляет ссылки, поэтому, хотя поле setf for имеет некоторый смысл, похоже, что оно может быть неправильным. - person Joshua Taylor; 22.05.2014
comment
Резюме: 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) для установки элемента внутри массива.

person Alex Martelli    schedule 15.05.2009
comment
Имеет смысл для имени setq. Легко запомнить. Спасибо. - person CDR; 09.12.2009

Вы можете использовать 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))
person dansalmo    schedule 01.08.2012

Можно думать о 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, которая является более общей конструкцией. Некоторые другие ответы рассказывают вам немного более сложную историю, когда мы принимаем во внимание макросы символов.

person Rainer Joswig    schedule 08.08.2013

Я хотел бы добавить к предыдущим ответам, что 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))
person Filipp    schedule 16.07.2012

person    schedule
comment
Я считаю, что ваш ответ более ясен, чем ответ, получивший наибольшее количество голосов. Большое спасибо. - person CDR; 09.12.2009
comment
@Sourav, пожалуйста, НИКОГДА не используйте букву l (ell) в качестве переменной или символа в примере кода. Его слишком сложно визуально отличить от цифры 1. - person DavidBooth; 20.12.2014
comment
Нет, я до сих пор не понимаю, как (car ls) может иметь значение l или нет. Вы понимаете, как перевести CLisp на C? а как написать интерпретатор CLisp? - person reuns; 16.11.2015
comment
@ user1952009 clisp - это одна из реализаций Common Lisp. Если вы хотите обратиться к самому языку, CL - наиболее часто используемое сокращение. - person ssice; 18.05.2016
comment
@ user1952009 Выражаясь языком C, car по существу разыменовывает указатель списка, давая вам ссылку на первый элемент (first делает то же самое). (setf (car ls) 10) может быть записано как *ls = 10; в C. - person Yay295; 13.11.2016
comment
@ Yay295, значит, вы говорите, что это L-значение, так что (setf (car ls) 5) совпадает с l->val = 5; в C. А как насчет (setf (car (car (car ls))) 5), если сказать (setq ls '(((1))) )? - person reuns; 13.11.2016
comment
@ user1952009 После (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