Яка різниця між "set", "setq" та "setf" у Common Lisp?
Яка різниця між "set", "setq" та "setf" у Common Lisp?
Відповіді:
Спочатку в Ліспі не було лексичних змінних - лише динамічних. І не було 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.
f
насправді стоїть функція , а не поле (або форма , для цього питання) і надає посилання, тому, хоча setf для поля має певний сенс, схоже, це може бути неправильним.
set
це функція. Таким чином, він не знає навколишнього середовища. set
не може бачити лексичну змінну. Він може встановлювати лише символ-значення свого аргументу. setq
більше не "встановлюється з цитатами". Це свідчить про те, що setq
це особлива форма, а не макрос.
(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
(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.
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))