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?


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.


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

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.


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

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.


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))
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.