Lisp-Auswertung von let-Anweisungen

8

Ich schreibe einen Scheme-Interpreter, und ich stehe vor einer gültigen let-Anweisung wie:

%Vor%

Mein Interpreter implementiert nur eine rein funktionale Teilmenge von Scheme, so dass es keine Nebenwirkungen wie set! gibt. Warum würden Sie in einer rein funktionalen Sprache mehrere Ausdrücke innerhalb einer let-Anweisung wie oben zulassen?

Und wenn ich meinen Dolmetscher schreibe, gibt es irgendeinen Grund, dass ich irgendetwas außer dem letzten Ausdruck im Let bewerten sollte? Es scheint, dass sie das Ergebnis der letzten evaluierten Aussage niemals beeinflussen könnten.

    
Kai 17.03.2009, 03:03
quelle

3 Antworten

2

Sie haben (fast) Recht: Wenn Sie eine rein funktionale Teilmenge von Scheme implementieren (dh keine set! , set-car! , set-cdr! ), dann hat jeder Ausdruck außer der letzten in let Ihr Rückgabewert wird weggeworfen, und da Sie garantiert keine Nebenwirkungen haben, besteht keine Gefahr, sie still zu ignorieren.

Es gibt jedoch einen kleinen Fall, den Sie beachten müssen, und das ist der Fall, wenn die vorhergehenden Ausdrücke define s:

sind %Vor%

Dies ist sowohl legal als auch funktional. Allerdings gibt es einige gute Nachrichten - innerhalb eines Blocks (wie ein let ) müssen Sie alle Ihre define s ganz oben haben. Wie in, dies gilt nicht als rechtliche Regelung:

%Vor%

Das bedeutet, dass Sie bei der Auswertung eines Blocks lediglich den oberen Teil nach define s durchsuchen und ihn in den entsprechenden Ausdruck letrec einpacken und dann alle bis auf den letzten Ausdruck ignorieren müssen (was Sie tun würden dann zurück).

bearbeiten: antti.huima macht einen ausgezeichneten Punkt über Anruf / cc. Wenn Sie Fortsetzungen in Ihre Implementierung einbeziehen, können Sie nicht viele Annahmen darüber treffen, wann die Dinge ausgewertet werden.

    
Kyle Cronin 17.03.2009, 04:08
quelle
6

Tatsächlich können Sie nicht alle bis auf die letzte Anweisung "löschen", da die vorherigen Anweisungen nicht terminierend sein können. Zum Beispiel:

%Vor%

Wenn Sie (func) unevaluated belassen, erhalten Sie das falsche Ergebnis (das ist 1), während Sie eine nicht terminierende Berechnung erhalten sollten.

Ein weiteres Problem ist, dass call / cc (call-with-current-continuation) (und ja, es gehört zur funktionalen Teilmenge) verwendet werden kann, um aus der Berechnung von nicht-tail zurückzugeben Position, zB:

%Vor%

was 3 zurückgibt und nicht 4. Dies ist immer noch rein funktional.

Beachten Sie, dass (let () x y z) der Einzelanweisungsform (let () (begin x y z)) entspricht, also ist die wirkliche Frage, ob Sie begin brauchen:)

    
Antti Huima 17.03.2009 04:13
quelle
1

Okay, die let erstellt gerade eine Bindung, wie zB define . Es gibt nichts, was eine gebundene Variable wie set! ändern würde. Überlegen Sie sich jetzt, was der Bereich Ihrer Namen ist: ist a von '(+ a b) the same as the a' an 4 gebunden? (Hinweis: nein.)

Der springende Punkt hier ist, dass Sie sich auch in kleinen Fällen wie diesem richtig verhalten müssen: Die Regeln für das Scoping und das Binden sind einfach und gut definiert und machen etwas, das verwirrend aussieht ist nur eine Konsequenz von ihnen. Es ist praktisch, weil Sie mit lexikalisch definierten Bindungen mit let klarere Programme schreiben können, selbst wenn es Fälle von perversen Seiten gibt.

aktualisieren Oh, ich habe einen Punkt weggelassen. Sie haben Recht, dass der (+ a b) -Aufruf keine nachhaltige Wirkung hat, aber dann können Sie im allgemeinen Fall nicht davon ausgehen, dass das wahr wäre, und Sie können nicht feststellen, ob es wahr ist, indem Sie den Programmtext dort allein untersuchen. (Bedenken Sie: Es könnte andere Funktionen anstelle von " + " geben.) Wenn Sie jedoch davon ausgehen, dass Sie das korrekte Ergebnis erhalten, ohne die verschiedenen let -Klauseln zu bewerten, verstehen Sie das nicht was es versucht, noch zu tun.

    
Charlie Martin 17.03.2009 03:38
quelle

Tags und Links