2014-04-11 14 views
7

Diese Passage aus On Lisp ist wirklich verwirrend - es ist nicht klar, wie eine zitierte Liste Rückkehr wie '(oh my) kann tatsächlich ändern, wie die Funktion in der Zukunft verhält: wird nicht die zurückgegebenen Liste Wird in der Funktion von Grund auf neu generiert, beim nächsten Aufruf?Bizarre zitierte Liste Beispiel von On Lisp

Wenn wir definieren ausrufen, so dass der Rückgabewert notierten Liste enthält,

(defun exclaim (expression) 
    (append expression ’(oh my))) 

Dann kann jede später destruktiv Änderung des Rückgabewertes

(exclaim ’(lions and tigers and bears)) 
-> (LIONS AND TIGERS AND BEARS OH MY) 
(nconc * ’(goodness)) 
-> (LIONS AND TIGERS AND BEARS OH MY GOODNESS) 

könnte die Liste verändern innerhalb der Funktion:

(exclaim ’(fixnums and bignums and floats)) 
-> (FIXNUMS AND BIGNUMS AND FLOATS OH MY GOODNESS) 

ausrufen Beweis gegen solche Probleme zu machen, soll es geschrieben werden:

(defun exclaim (expression) 
    (append expression (list ’oh ’my))) 

Wie genau ist der letzte Aufruf an exclaimgoodness zu dem Ergebnis, das Wort hinzufügen? Die Funktion referenziert keine externe Variable. Wie also hat der separate Aufruf an nconc die Funktion der exclaim Funktion verändert?

+0

Ich sehe, Sie haben eine Antwort, aber werfen Sie einen Blick auf [diese Antwort] (http://Stackoverflow.com/a/18790523/1281433) für einige zusätzliche Diskussion und Ressourcen –

+1

Auch dies ist wahrscheinlich ein Duplikat von [Strange Lisp Quoting-Szenario - Graham's On Lisp, Seite 37] (http://stackoverflow.com/q/4003115/1281433). –

Antwort

8

a) Die Auswirkungen der Änderung Literal Listen ist nicht definiert in der Common-Lisp-Standard. Was Sie hier als Beispiel sehen, ist ein mögliches Verhalten.

(1 2 3 4) ist eine wörtliche Liste. Aber ein Anruf an LIST wie in (list 1 2 3 4) gibt eine frisch Consed-Liste zur Laufzeit zurück.

b) Die Liste ist wörtliche Daten im Code der Funktion. Jeder Aufruf gibt genau dieses Datenobjekt zurück. Wenn Sie bei jedem Anruf eine neue Liste bereitstellen möchten, müssen Sie beispielsweise LIST oder COPY-LIST verwenden.

c) Da die zurückgegebene Liste immer das gleiche literale Datenobjekt ist, kann die Änderung diesen Effekt wie beschrieben haben. Man könnte sich auch vorstellen, dass ein Fehler auftritt, wenn der Code und seine Objekte in einem Nur-Lese-Speicher zugeordnet sind. Das Ändern der Liste würde dann versuchen, in einen Nur-Lese-Speicher zu schreiben.

d) Bei der Arbeit mit literalen Listendaten im Quellcode ist folgendes zu beachten: Der Lisp-Compiler kann den Speicher frei optimieren. Wenn eine Liste im Quellcode mehrfach vorkommt, darf ein Compiler dies erkennen und nur EINE Liste erstellen. Alle verschiedenen Orte würden dann auf diese eine Liste zeigen. Eine Änderung der Liste hätte somit zur Folge, dass diese Änderungen an mehreren Stellen sichtbar sind.

Dies kann auch bei anderen literalen Datenobjekten wie Arrays/Vektoren vorkommen.

Wenn Ihre Datenstruktur ein Teil des Codes ist, geben Sie diese interne Datenstruktur zurück, Sie ändern diese Datenstruktur - dann versuchen Sie, Ihren Code zu ändern.

Beachten Sie auch, dass Lisp von einem Interpreter ausgeführt werden kann. Der Interpreter arbeitet normalerweise mit der Lisp-Quellstruktur - der Code ist kein Maschinencode, sondern interpretiert Lisp-Code als Lisp-Daten. Hier können Sie möglicherweise den Quellcode zur Laufzeit ändern, nicht nur die im Quellcode eingebetteten Daten.

+0

also du sagst, dass das '' (oh mein) 'weiterlebt, nachdem die Funktion zurückkehrt, weiterlebt als ein tatsächliches Stück persistente Daten, und dass der' nconc' diese Daten mutiert und es macht '' (oh mein Gott) 'so dass, wenn' exclaim' erneut ausgeführt wird, es einen neuen Wert in diesen Daten verwendet? – johnbakers

+0

Ja. 'exclaim' selbst mutiert nichts, was mit' nconc' gemacht wird. 'exclaim' macht lediglich den neuen Inhalt der geänderten zitierten Liste sichtbar. – gsg

+0

@Rainer: Ist (a) für alle Listen oder nur Literallisten wahr? – Baggers