Різниця між `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
Звичайний Лісп завжди мав лексичні змінні. Ви мусите говорити про якогось Ліспа перед загальним Лісом.
Rainer Joswig

4
Якщо SET і SETQ повинні бути завантажені у звичайного спадкоємця Lisp, їм доведеться отримати деяку заміну. Їх використання у коді високого рівня обмежене, але код низького рівня (наприклад, код SETF реалізований у) потребує їх.
Svante

13
Чи є причина, що ви вибрали "автомобіль" як поле замість того, що може заплутатися як функція автомобіля?
друдру

9
Ця відповідь на те, на чому позначається f у setf? стверджує, що fнасправді стоїть функція , а не поле (або форма , для цього питання) і надає посилання, тому, хоча setf для поля має певний сенс, схоже, це може бути неправильним.
Джошуа Тейлор

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
@Sourav, будь ласка, НІКОЛИ не використовуйте букву "l" (ell) як змінну чи символ у прикладі коду. Це занадто важко візуально відрізнити від цифри 1.
DavidBooth

Ні, я досі не розумію, як (автомобіль ls) може бути l-значенням чи ні. Ви розумієте, як перекласти CLisp на C? і як написати перекладача CLisp?
reuns

@ user1952009 clisp - це одна загальна реалізація 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у C.
Кайл

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.