Ich bringe mir etwas mehr Elisp bei und bin auf folgendes Problem gestoßen:
Wenn ich eine Listenvariable zurücksetzen möchte, wird sie nach der ersten Auswertung nicht aktualisiert. Hier ist ein Beispielcode:
(defun initilize ()
(setq example '(3)))
(defun modify ()
(initilize)
(message "%S" example)
(setcar example 2))
; M-x eval-buffer RET
(modify) ; message --> (3)
(modify) ; message --> (2)
(modify) ; message --> (2)
Ich interessiere mich für zwei Dinge. Das erste ist, mehr darüber zu erfahren, was "unter der Haube" passiert. Warum funktioniert es also beim ersten Mal und schlägt bei nachfolgenden Anrufen fehl?
Die zweite und praktischere Frage ist, wie die Liste richtig neu initialisiert werden kann, oder gibt es eine andere übliche Methode, um so etwas zu tun?
Eine Problemumgehung, die ich selbst gefunden habe, besteht darin, eine zitierte Liste zu verwenden und den Inhalt folgendermaßen zu bewerten:
(setq example `(,3))
list
quote
mutability
Clemera
quelle
quelle
'(some list)
zu sein ,eq
um'(some list)
- je .Es ist in der Regel keine Garantie in Lisp , dass Code, der sichtbar zitiert eine Liste zurückgibt neue Listenstruktur jedes Mal. In einigen Lisp-Implementierungen kann dies der Fall sein oder manchmal. Bei anderen ist dies niemals der Fall. Ihr Code sollte ohnehin nicht von einem solchen Verhalten der Implementierung abhängen. Wenn Sie eine neue Listenstruktur wünschen, verwenden Sielist
odercons
oder eine gleichwertige.example
es nie als Variable deklariert wurde, also so tunsetq
muss, als ob es eine neue Variable deklariert, aber später, wenn Sieinitialize
erneut aufrufen, wird eine neue Variable erstellt, währendmodify
die alte gespeichert wird ... In jedem Fall ist dies kein erwartetes Verhalten. Die Verwendung vonsetq
etwas, das zuvor nicht als Variable eingeführt wurde, kann jedoch genauso gut undefiniert sein.'(3)
wird als wörtlicher Wert behandelt.(setcar '(3) 2)
Wenn Sie also einmal , wann immer Sie dies tun(defvar foo '(3))
oder(let ((foo '(3)))
so weiter, erhalten Sie wahrscheinlich einen Wert vonfoo
gleich'(2)
. Ich sage "wahrscheinlich", weil dieses Verhalten nicht garantiert ist. Es ist eine Art Optimierung, die der Interpreter immer dann durchführt, wenn er Lust hat, etwas, das als Eliminierung von Konstanten-Subausdrücken bekannt ist (ein besonderer Fall von). Was abo-abo schrieb, ist also nicht genau der Grund. Es ist eher so, als würde man ein String-Literal in C ändern (was normalerweise eine Warnung generiert).Antworten:
Vielleicht klärt dies die Verwirrung auf:
Ihre Funktion
initilize
initialisiert keine Variablenexample
. Es setzt es auf eine bestimmte Nachteile-Zelle - bei jedem Aufruf dieselbe Nachteile-Zelle. Beim ersteninitilize
Aufruf wird diesetq
Zuweisungexample
einer neuen Nachteile-Zelle zugewiesen, die das Ergebnis der Auswertung ist'(3)
. Nachfolgende Aufrufe müsseninitilize
nurexample
derselben Cons-Zelle zugewiesen werden .Da
initilize
nur dieselbe Nachteile-Zelle neu zugewiesen wird,example
wirdmodify
das Auto derselben Nachteile-Zelle bei2
jedem Aufruf einfach auf gesetzt.Verwenden Sie zum Initialisieren einer Liste
list
odercons
(oder ein gleichwertiges Backquote-Sexp wie`(,(+ 2 1))
oder`(,3)
). Verwenden Sie zum Beispiel(list 3)
.Der Schlüssel zum Verständnis besteht darin, zu wissen, dass eine zitierte Cons-Zelle nur einmal ausgewertet wird und danach dieselbe Cons-Zelle zurückgegeben wird. Dies ist nicht unbedingt die Art und Weise, wie sich alle Lisps verhalten, aber so verhält sich Emacs Lisp.
Allgemeiner ist das Verhalten beim Auswerten eines zitierten veränderlichen Objekts implementierungsabhängig, wenn nicht sprachabhängig. In Common Lisp zum Beispiel bin ich mir ziemlich sicher, dass die Definition (Spezifikation) der Sprache nichts enthält, was das diesbezügliche Verhalten definiert - es bleibt der Implementierung überlassen.
Zusammenfassung: Erwarten Sie nicht, dass '(eine Liste) gleich' (eine Liste) ist. Es gibt im Allgemeinen keine Garantie in Lisp, dass Code, der eine Liste sichtbar zitiert, jedes Mal eine neue Listenstruktur zurückgibt. In einigen Lisp-Implementierungen kann dies der Fall sein oder manchmal. Bei anderen ist dies niemals der Fall. Ihr Code sollte ohnehin nicht von einem solchen Verhalten der Implementierung abhängen. Wenn Sie eine neue Listenstruktur wünschen, verwenden Sie
list
odercons
oder eine gleichwertige.quelle
Sie können
(setq example (list 3))
diesen Fehler vermeiden.Was passiert ist ,
init
ordnet ein Objekt , das zunächst enthält(3)
zuexample
. Der Wert des Objekts wird nur einmal festgelegt. Anschließend ändern Sie diesen Wert.Hier ist Ihr Beispiel in C ++, wenn Sie das besser verstehen:
quelle
initilize
Funktion neu bewerte undmodify
erneut aufrufe, wird(3)
sie nur wieder angezeigt, weil ich die Funktion neu bewertet habe.(3)
als temporäres Objekt vor, das Teil davon istinit
.init
Der Körper von 'setztexample
auf die Adresse dieses temporären Objekts, er berührt seinen Wert nicht.