Różnica między `set`,` setq` i `setf` w Common Lisp?

166

Jaka jest różnica między „set”, „setq” i „setf” w Common Lisp?

Richard Hoskins
źródło
9
Odpowiedzi na ich zachowanie są dość dobre, ale zaakceptowana odpowiedź ma prawdopodobnie błędną etymologię dla „f” w „setf”. Odpowiedź na pytanie, co oznacza f w setf? mówi, że jest dla „funkcji” i podaje odniesienia do jej tworzenia.
Joshua Taylor

Odpowiedzi:

169

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.

programista stosu
źródło
45
Common Lisp zawsze miał zmienne leksykalne. Musisz mówić o jakimś Lispie przed Common Lispem.
Rainer Joswig
4
Jeśli SET i SETQ mają być ładowane z następcy Common Lisp, będą musiały uzyskać zamiennik. Ich użycie w kodzie wysokopoziomowym jest ograniczone, ale kod niskiego poziomu (na przykład kod SETF jest zaimplementowany) ich potrzebuje.
Svante
13
Czy jest powód, dla którego wybrałeś „samochód” jako pole zamiast czegoś, co mogłoby zostać pomylone z funkcją samochodu?
drudru
9
Ta odpowiedź na pytanie, co oznacza f w setf? twierdzi, że ffaktycznie 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.
Joshua Taylor
1
Podsumowanie: setjest funkcją. Dlatego nie zna środowiska. setnie widzi zmiennej leksykalnej. Może ustawić tylko symbol-wartość swojego argumentu. setqnie jest już „cytowany”. Świadczy o tym fakt, że setqjest to specjalna forma, a nie makro.
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
źródło
12
Uważam, że twoja odpowiedź jest bardziej jasna niż ta, na którą głosowano najczęściej. Wielkie dzięki.
CDR
2
@Sourav, proszę NIGDY nie używać litery „l” (ell) jako zmiennej lub symbolu w przykładowym kodzie. Jest zbyt trudno wizualnie odróżnić od cyfry 1.
DavidBooth,
Nie, nadal nie rozumiem, jak (samochód ls) może być wartością l, czy nie. Czy rozumiesz, jak przetłumaczyć CLisp na C? i jak napisać interpreter CLisp?
wraca
@ user1952009 clisp jest jedną z implementacji Common Lisp. Jeśli chcesz odnieść się do samego języka, najczęściej używanym skrótem jest CL.
raz
2
@ user1952009 Po (setq ls '(((1)))), (setf (car (car (car ls))) 5)jest zdefiniowana zachowania, ponieważ wartość lsjest 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 = 5w C
Kyle
21

setqjest jak setw przypadku pierwszego argumentu w cudzysłowie - (set 'foo '(bar baz))jest jak (setq foo '(bar baz)). setfz 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 setfprzyjmuje pierwszy argument jako "odniesienie", więc np. (aref myarray 3)zadziała (jako pierwszy argument setf) ustawiając element wewnątrz tablicy.

Alex Martelli
źródło
1
Ma najwięcej sensu dla nazwy setq. Łatwe do zapamiętania. Dzięki.
CDR
17

Możesz użyć setfzamiast setlub, setqale nie odwrotnie, ponieważ setfmoż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)

setfma dodatkową możliwość ustawiania elementu listy w foonowej 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ć, defvarjeś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))
dansalmo
źródło
13

Można myśleć SETi SETQbyć konstruktami niskiego poziomu.

  • SET można ustawić wartość symboli.

  • SETQ może ustawić wartość zmiennych.

Następnie SETFjest 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ę SETFrozwijało do SETi SETQ.

* (macroexpand '(setf (symbol-value 'a) 10))

(SET 'A 10)


* (macroexpand '(setf a 10))         

(SETQ A 10)

Tak więc SETi SETQsą 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.

Rainer Joswig
źródło
4

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))
Filipp
źródło