Warum gibt diese function jedes Mal einen anderen Wert zurück?

Kann jemand das folgende Verhalten erklären? Warum gibt die function speziell jedes Mal eine andere Liste zurück? Warum wird bei jedem Aufruf der function nicht some-list mit '(0 0 0) initialisiert?

 (defun foo () (let ((some-list '(0 0 0))) (incf (car some-list)) some-list)) 

Ausgabe:

 > (foo) (1 0 0) > (foo) (2 0 0) > (foo) (3 0 0) > (foo) (4 0 0) 

Vielen Dank!

BEARBEITEN:

Welche Art der Implementierung dieser function wird empfohlen, vorausgesetzt, dass die function jedes Mal '(1 0 0) ausgeben soll?

'(0 0 0) ist ein Literalobjekt, von dem angenommen wird, dass es eine Konstante ist (wenn auch nicht vor Änderungen geschützt). Sie modifizieren also jedes Mal das gleiche Objekt. Um bei jedem functionsaufruf unterschiedliche Objekte anzulegen, verwenden Sie (list 0 0 0) .

Wenn Sie also nicht wissen, was Sie tun, sollten Sie Literallisten (wie '(0 0 0) ) immer nur als Konstanten verwenden.

Nebenbei bemerkt, wenn Sie diese function in der sbcl REPL definieren, erhalten Sie folgende Warnung:

  caught WARNING: Destructive function SB-KERNEL:%RPLACA called on constant data. See also: The ANSI Standard, Special Operator QUOTE The ANSI Standard, Section 3.2.2.3 

Was einen guten Hinweis auf das Problem gibt.

'(0 0 0) im Code sind Literaldaten. Das Ändern dieser Daten hat ein nicht definiertes Verhalten. Common-Lisp-Implementierungen können es zur Laufzeit möglicherweise nicht erkennen (es sei denn, Daten werden zum Beispiel in einen Nur-Lese-Speicherbereich gelegt). Aber es kann unerwünschte Wirkungen haben.

  • Sie sehen, dass diese Daten über verschiedene Aufrufe derselben function verteilt sein können

  • Einer der subtileren möglichen Fehler ist dies: Common Lisp wurde mit verschiedenen Optimierungen definiert, die von einem Compiler berücksichtigt werden können. Zum Beispiel darf ein Compiler Daten wiederverwenden:

Beispiel:

 (let ((a '(1 2 3)) (b '(1 2 3))) (list ab)) 

Im obigen Codefragment kann der Compiler erkennen, dass die Literaldaten von a und b sind. Es kann dann beide Variablen auf die gleichen wörtlichen Daten zeigen. Das Ändern kann funktionieren, aber die Änderung ist von a und b aus sichtbar.

Zusammenfassung: Die Änderung von Literaldaten ist eine Quelle für mehrere kleine Fehler. Vermeiden Sie es wenn möglich. Dann müssen Sie neue Datenobjekte schützen. Consing bedeutet im Allgemeinen die Zuweisung von frischen, neuen Datenstrukturen zur Laufzeit.

Ich wollte selbst einen schreiben, aber ich habe einen guten online gefunden:

CommonLisp hat erstklassige functionen, dh functionen sind Objekte, die zur Laufzeit erstellt und als Argumente an andere functionen übergeben werden können. –AlainPicard Diese erstklassigen functionen haben auch ihren eigenen Zustand, sie sind also Funktoren. Alle Lisp-functionen sind Funktoren; Es gibt keine Trennung zwischen functionen, die “nur Code” und “functionsobjekte” sind. Der Zustand nimmt die Form von gefangenen lexikalischen Variablenbindungen an. Sie müssen LAMBDA nicht zum Erfassen von Bindungen verwenden. ein Top-Level-DEFUN kann es auch tun: (lassen ((private-Variable 42)) (defun foo () …))

Der Code an Stelle von … sieht private-Variable in ihrem lexikalischen scope. Es gibt eine Instanz dieser Variablen, die dem einzigen functionsobjekt zugeordnet ist, das global an das Symbol FOO gebunden ist; Die Variable wird zu dem Zeitpunkt erfasst, zu dem der DEFUN-Ausdruck ausgewertet wird. Diese Variable verhält sich dann in C wie eine statische Variable. Alternativ können Sie sich FOO als “Singleton” -Objekt mit einer “Instanzvariablen” vorstellen. –KazKylheku

Ref http://c2.com/cgi/wiki?CommonLisp