Jaka jest różnica między „set”, „setq” i „setf” w Common Lisp?
Jaka jest różnica między „set”, „setq” i „setf” w Common Lisp?
Odpowiedzi:
Pierwotnie w Lispie nie było zmiennych leksykalnych - tylko dynamiczne. I nie było SETQ ani SETF, tylko funkcja SET.
Co teraz jest napisane jako:
(setf (symbol-value '*foo*) 42)
został napisany jako:
(set (quote *foo*) 42)
który ostatecznie został skrócony do SETQ (SET Quoted):
(setq *foo* 42)
Potem pojawiły się zmienne leksykalne i SETQ zaczęło być używane również do przypisania do nich - więc nie było to już proste opakowanie wokół SET.
Później ktoś wymyślił SETF (SET Field) jako ogólny sposób przypisywania wartości do struktur danych, aby odzwierciedlić l-wartości innych języków:
x.car := 42;
zostanie zapisane jako
(setf (car x) 42)
Aby uzyskać symetrię i ogólność, SETF zapewnił również funkcjonalność SETQ. W tym momencie należałoby powiedzieć, że SETQ był prymitywem niskiego poziomu, a SETF operacją wysokiego poziomu.
Wtedy pojawiły się makra symboli. Aby makra symboli mogły działać w sposób przezroczysty, zdano sobie sprawę, że SETQ musiałby działać jak SETF, gdyby przypisywana „zmienna” była w rzeczywistości makrem symbolu:
(defvar *hidden* (cons 42 42))
(define-symbol-macro foo (car *hidden*))
foo => 42
(setq foo 13)
foo => 13
*hidden* => (13 . 42)
Dochodzimy więc do teraźniejszości: SET i SETQ to zanikłe pozostałości starszych dialektów i prawdopodobnie zostaną uruchomione z ewentualnych następców Common Lisp.
f
faktycznie oznacza funkcję , a nie pole (lub formularz , jeśli o to chodzi) i dostarcza odniesienia, więc chociaż setf dla pola ma jakiś sens, wygląda na to, że może nie być poprawny.
set
jest funkcją. Dlatego nie zna środowiska. set
nie widzi zmiennej leksykalnej. Może ustawić tylko symbol-wartość swojego argumentu. setq
nie jest już „cytowany”. Świadczy o tym fakt, że setq
jest to specjalna forma, a nie makro.
(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)
jest zdefiniowana zachowania, ponieważ wartość ls
jest stałe (na przykład modyfikując ciągiem znaków w C). Po (setq ls (list (list (list 1))))
, (setf (car (car (car ls))) 5)
działa podobnie jak ls->val->val->val = 5
w C
setq
jest jak set
w przypadku pierwszego argumentu w cudzysłowie - (set 'foo '(bar baz))
jest jak (setq foo '(bar baz))
. setf
z drugiej strony jest rzeczywiście subtelny - to jak „pośrednictwo”. Proponuję http://www.nano.com/lisp/cmucl-tutorials/LISP-tutorial-16.html jako lepszy sposób na rozpoczęcie rozumienia tego, niż jakakolwiek odpowiedź tutaj może dać ... w skrócie, jednak setf
przyjmuje pierwszy argument jako "odniesienie", więc np. (aref myarray 3)
zadziała (jako pierwszy argument setf
) ustawiając element wewnątrz tablicy.
Możesz użyć setf
zamiast set
lub, setq
ale nie odwrotnie, ponieważ setf
możesz również ustawić wartość poszczególnych elementów zmiennej, jeśli zmienna ma indywidualne elementy. Zobacz przykłady poniżej:
Wszystkie cztery przykłady przypisują listę (1, 2, 3) do zmiennej o nazwie 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
ma dodatkową możliwość ustawiania elementu listy w foo
nowej wartości.
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)
Można jednak zdefiniować makro symbolu, które będzie reprezentować pojedynczy element w 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)
Możesz użyć, defvar
jeśli nie zdefiniowałeś jeszcze zmiennej i nie chcesz nadawać jej wartości aż do późniejszej części kodu.
(defvar foo2)
(define-symbol-macro foo-car (car foo2))
Można myśleć SET
i SETQ
być konstruktami niskiego poziomu.
SET
można ustawić wartość symboli.
SETQ
może ustawić wartość zmiennych.
Następnie SETF
jest makro, które zapewnia wiele rodzajów ustawień: symbole, zmienne, elementy tablicy, miejsca na instancje, ...
W przypadku symboli i zmiennych można pomyśleć, jakby się SETF
rozwijało do SET
i SETQ
.
* (macroexpand '(setf (symbol-value 'a) 10))
(SET 'A 10)
* (macroexpand '(setf a 10))
(SETQ A 10)
Tak więc SET
i SETQ
są używane do implementacji niektórych funkcji SETF
, które są bardziej ogólną konstrukcją. Niektóre z pozostałych odpowiedzi przedstawiają nieco bardziej złożoną historię, gdy weźmiemy pod uwagę makra symboli.
Chciałbym dodać do poprzednich odpowiedzi, że setf to makro, które wywołuje określoną funkcję w zależności od tego, co zostało przekazane jako pierwszy argument. Porównaj wyniki rozwijania makra setf z różnymi typami argumentów:
(macroexpand '(setf a 1))
(macroexpand '(setf (car (list 3 2 1)) 1))
(macroexpand '(setf (aref #(3 2 1) 0) 1))
Dla niektórych typów argumentów "funkcja setf" będzie nazywana:
(defstruct strct field)
(macroexpand '(setf (strct-field (make-strct)) 1))