Was ist der Unterschied zwischen "set", "setq" und "setf" in Common Lisp?
Was ist der Unterschied zwischen "set", "setq" und "setf" in Common Lisp?
Antworten:
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.
f
tatsä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.
set
ist eine Funktion. Somit kennt es die Umgebung nicht. set
lexikalische Variable kann nicht angezeigt werden. Es kann nur den Symbolwert seines Arguments festlegen. setq
wird nicht mehr "gesetzt" gesetzt. Die Tatsache, dass setq
es sich um eine spezielle Form und nicht um ein Makro handelt, zeigt dies.
(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)
ist undefiniertes Verhalten, weil der Wert der ls
Konstante (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 = 5
in C
setq
ist genau wie set
bei einem zitierten ersten Argument - (set 'foo '(bar baz))
ist genau so (setq foo '(bar baz))
. setf
Auf 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, setf
nimmt das erstes Argument als "Referenz", so dass zB (aref myarray 3)
(als erstes Argument setf
) ein Element innerhalb eines Arrays festgelegt wird.
Sie können setf
anstelle set
oder setq
aber nicht umgekehrt verwenden, da Sie setf
auch 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)
setf
hat die zusätzliche Möglichkeit, ein Mitglied der Liste foo
auf 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, defvar
wenn 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))
Man kann sich Konstrukte auf niedriger Ebene vorstellen SET
und SETQ
diese sein.
SET
kann den Wert von Symbolen einstellen.
SETQ
kann den Wert von Variablen einstellen.
Dann SETF
gibt 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 SETF
sich in SET
und ausdehnen SETQ
.
* (macroexpand '(setf (symbol-value 'a) 10))
(SET 'A 10)
* (macroexpand '(setf a 10))
(SETQ A 10)
Also SET
und SETQ
werden 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.
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))