Unterschied zwischen `set`,` setq` und `setf` in Common Lisp?


165

Was ist der Unterschied zwischen "set", "setq" und "setf" in Common Lisp?


9
Das Verhalten dieser wird in den Antworten ziemlich gut beantwortet, aber die akzeptierte Antwort hat eine möglicherweise falsche Etymologie für das "f" in "setf". Die Antwort auf Wofür steht das f in setf? sagt, dass es für "Funktion" ist, und gibt Verweise zum Sichern.
Joshua Taylor

Antworten:


169

Ursprünglich gab es in Lisp keine lexikalischen Variablen - nur dynamische. Und es gab kein SETQ oder SETF, nur die SET-Funktion.

Was ist jetzt geschrieben als:

(setf (symbol-value '*foo*) 42)

wurde geschrieben als:

(set (quote *foo*) 42)

was schließlich mit SETQ (SET Quoted) abgekürzt wurde:

(setq *foo* 42)

Dann passierten lexikalische Variablen, und SETQ wurde auch für die Zuweisung verwendet - es war also kein einfacher Wrapper mehr um SET.

Später erfand jemand SETF (SET Field) als generische Methode zum Zuweisen von Werten zu Datenstrukturen, um die l-Werte anderer Sprachen widerzuspiegeln:

x.car := 42;

würde geschrieben werden als

(setf (car x) 42)

Aus Gründen der Symmetrie und Allgemeinheit stellte SETF auch die Funktionalität von SETQ bereit. Zu diesem Zeitpunkt wäre es richtig gewesen zu sagen, dass SETQ ein Grundelement auf niedriger Ebene und SETF eine Operation auf hoher Ebene war.

Dann passierten Symbolmakros. Damit Symbolmakros transparent funktionieren können, wurde erkannt, dass SETQ sich wie SETF verhalten muss, wenn die zugewiesene "Variable" wirklich ein Symbolmakro ist:

(defvar *hidden* (cons 42 42))
(define-symbol-macro foo (car *hidden*))

foo => 42

(setq foo 13)

foo => 13

*hidden* => (13 . 42)

So kommen wir in der Gegenwart an: SET und SETQ sind verkümmerte Überreste älterer Dialekte und werden wahrscheinlich von späteren Nachfolgern von Common Lisp gebootet.


45
Common Lisp hatte immer lexikalische Variablen. Sie müssen vor Common Lisp über etwas Lisp sprechen.
Rainer Joswig

4
Wenn SET und SETQ von einem Common Lisp-Nachfolger gebootet werden sollen, müssen sie ersetzt werden. Ihre Verwendung in High-Level-Code ist begrenzt, aber Low-Level-Code (zum Beispiel der Code, in dem SETF implementiert ist) benötigt sie.
Svante

13
Gibt es einen Grund, warum Sie "Auto" als Feld gewählt haben, anstatt etwas, das als Autofunktion verwirrt werden könnte?
Drudru

9
Diese Antwort auf Wofür steht das f in setf? behauptet, dass das ftatsächlich für Funktion und nicht für Feld (oder Form ) steht und Referenzen bereitstellt. Obwohl die Menge für Feld sinnvoll ist, sieht es so aus, als ob sie möglicherweise nicht korrekt ist.
Joshua Taylor

1
Zusammenfassung: setist eine Funktion. Somit kennt es die Umgebung nicht. setlexikalische Variable kann nicht angezeigt werden. Es kann nur den Symbolwert seines Arguments festlegen. setqwird nicht mehr "gesetzt" gesetzt. Die Tatsache, dass setqes sich um eine spezielle Form und nicht um ein Makro handelt, zeigt dies.
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
Ich finde Ihre Antwort klarer als die am besten gewählte. Vielen Dank.
CDR

2
@Sourav, bitte verwenden Sie NIEMALS den Buchstaben "l" (ell) als Variable oder Symbol im Beispielcode. Es ist zu schwer, visuell von der Ziffer 1 zu unterscheiden.
DavidBooth

Nein, ich verstehe immer noch nicht, wie (Auto ls) ein l-Wert sein kann oder nicht. Verstehst du, wie man CLisp in C übersetzt? und wie schreibe ich einen CLisp-Interpreter?
Wiedervereinigungen

@ user1952009 clisp ist eine Common Lisp-Implementierung. Wenn Sie sich auf die Sprache selbst beziehen möchten, geben Sie CL die am häufigsten verwendete Abkürzung an.
ssice

2
@ Nach user1952009 (setq ls '(((1)))), (setf (car (car (car ls))) 5)ist undefiniertes Verhalten, weil der Wert der lsKonstante (wie ein Stringliteral in C Modifizierung) ist. Nach dem (setq ls (list (list (list 1)))), (setf (car (car (car ls))) 5)funktioniert genauso wie ls->val->val->val = 5in C
kyle

21

setqist genau wie setbei einem zitierten ersten Argument - (set 'foo '(bar baz))ist genau so (setq foo '(bar baz)). setfAuf der anderen Seite ist es in der Tat subtil - es ist wie eine "Indirektion". Ich schlage vor, http://www.nano.com/lisp/cmucl-tutorials/LISP-tutorial-16.html als einen besseren Weg, um es zu verstehen, als jede Antwort hier geben kann ... kurz gesagt, setfnimmt das erstes Argument als "Referenz", so dass zB (aref myarray 3)(als erstes Argument setf) ein Element innerhalb eines Arrays festgelegt wird.


1
Am sinnvollsten für den Setq-Namen. Leicht zu erinnern. Vielen Dank.
CDR

17

Sie können setfanstelle setoder setqaber nicht umgekehrt verwenden, da Sie setfauch den Wert einzelner Elemente einer Variablen festlegen können, wenn die Variable einzelne Elemente enthält. Siehe die folgenden Beispiele:

Alle vier Beispiele weisen die Liste (1, 2, 3) der Variablen mit dem Namen foo zu.

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

setfhat die zusätzliche Möglichkeit, ein Mitglied der Liste fooauf einen neuen Wert zu setzen.

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)

Sie können jedoch ein Symbolmakro definieren, das ein einzelnes Element in darstellt 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)

Sie können verwenden, defvarwenn Sie die Variable noch nicht definiert haben und ihr erst später in Ihrem Code einen Wert geben möchten.

(defvar foo2)
(define-symbol-macro foo-car (car foo2))

13

Man kann sich Konstrukte auf niedriger Ebene vorstellen SETund SETQdiese sein.

  • SET kann den Wert von Symbolen einstellen.

  • SETQ kann den Wert von Variablen einstellen.

Dann SETFgibt es ein Makro, das viele Arten von Einstellungsmöglichkeiten bietet: Symbole, Variablen, Array-Elemente, Instanz-Slots, ...

Bei Symbolen und Variablen kann man denken, als würde man SETFsich in SETund ausdehnen SETQ.

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

(SET 'A 10)


* (macroexpand '(setf a 10))         

(SETQ A 10)

Also SETund SETQwerden verwendet, um einige der Funktionen von zu implementieren SETF, die das allgemeinere Konstrukt ist. Einige der anderen Antworten erzählen Ihnen die etwas komplexere Geschichte, wenn wir Symbolmakros berücksichtigen.


4

Ich möchte zu früheren Antworten hinzufügen, dass setf ein Makro ist, das eine bestimmte Funktion aufruft, je nachdem, was als erstes Argument übergeben wurde. Vergleichen Sie die Ergebnisse der Makroerweiterung von setf mit verschiedenen Arten von Argumenten:

(macroexpand '(setf a 1))

(macroexpand '(setf (car (list 3 2 1)) 1))

(macroexpand '(setf (aref #(3 2 1) 0) 1))

Für einige Arten von Argumenten wird "setf function" aufgerufen:

(defstruct strct field)
(macroexpand '(setf (strct-field (make-strct)) 1))
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.