Разница между `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

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))

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))
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.