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

166

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

Ричард Хоскинс
источник
9
Поведение этих ответов довольно хорошо отражено в ответах, но принятый ответ имеет, возможно, ошибочную этимологию для «f» в «setf». Ответ на что означает f в setf? говорит, что это для "функции", и предоставляет ссылки для его резервного копирования.
Джошуа Тейлор

Ответы:

169

Первоначально в Лиспе не было лексических переменных - только динамические. И не было никакого 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.

программист стека
источник
45
У Common Lisp всегда были лексические переменные. Вы должны говорить о каком-то Лиспе перед Common Lisp.
Райнер Йосвиг
4
Если SET и SETQ должны быть загружены из преемника Common Lisp, они должны получить некоторую замену. Их использование в коде высокого уровня ограничено, но код низкого уровня (например, в котором реализован код SETF) нуждается в них.
Сванте
13
есть ли причина, по которой вы выбрали «автомобиль» в качестве поля вместо того, что могло бы привести к путанице в качестве функции автомобиля?
drudru
9
Этот ответ на что означает f в setf? утверждает, что на fсамом деле обозначает функцию , а не поле (или форму , если на то пошло), и предоставляет ссылки, поэтому, хотя setf for field имеет некоторый смысл, похоже, что оно может быть неверным.
Джошуа Тейлор
1
Резюме: setэто функция. Таким образом, он не знает окружающей среды. setне может видеть лексическую переменную. Он может установить только значение символа своего аргумента. setqбольше не «ставить в кавычки». Тот факт, что setqэто особая форма, а не макрос, показывает это.
KIM Taegyoon
142
(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
Sourav
источник
12
Я считаю, что ваш ответ будет более четким, чем голосование сверху. Большое спасибо.
CDR
2
@ Сурав, пожалуйста, НИКОГДА не используйте букву «l» (ell) в качестве переменной или символа в примере кода. Это слишком сложно визуально отличить от цифры 1.
DavidBooth
Нет, я все еще не понимаю, как (автомобиль ls) может быть значением l или нет. Вы понимаете, как перевести CLisp на C? а как написать интерпретатор CLisp?
восходит
@ user1952009 clisp - это одна из реализаций Common Lisp. Если вы хотите обратиться к самому языку, то CL чаще всего используется аббревиатура.
ssice
2
@ 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в С.
Kyle
21

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), чтобы установить элемент внутри массива.

Алекс Мартелли
источник
1
Имеет смысл для имени setq. Легко вспомнить. Спасибо.
CDR
17

Вы можете использовать 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))
dansalmo
источник
13

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

Райнер Йосвиг
источник
4

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