Ich lese den folgenden Abschnitt von SICP
Laut dem Text führt die folgende Umwandlung von eval
zu einer Leistungsverbesserung, da ein Ausdruck, der viele Male ausgewertet wird, nur einmal analysiert wird?
Hier ist eine analyze
-Funktion im Buch angegeben:
Ich verstehe nicht, warum das Buch sagt, dass analyze
nur einmal ausgeführt wird. Ist nicht der Body von eval
, was ((analyze exp) env))
ist, grundsätzlich zu sagen, dass jedes Mal, wenn eval
aufgerufen wird, analyze
mit exp
als Parameter aufgerufen wird? Dies würde bedeuten, dass analyze
jedes Mal aufgerufen wird, wenn eval
aufgerufen wird.
Was stimmt nicht mit meinem Verständnis? Ich würde mich über Feedback freuen, danke!
Tatsächlich wird bei jedem Aufruf von eval
mit Programmcode als Parameter der syntaktische Evaluator aufgerufen. Wenn jedoch eine Funktion innerhalb dieses Codes eine andere Funktion in diesem Code aufruft (oder sich im einfachsten Fall selbst durch Rekursion aufruft), erhält das innere apply
den analysierten Ausdruck (der am Ende eine Lambda-Funktion ist) als ein Argument, anstatt ein Blob von Code, der syntaktisch erneut analysiert werden müsste, um ausgeführt zu werden.
Gintautas 'Antwort ist richtig, aber vielleicht ist ein Beispiel in Ordnung. Angenommen, Sie haben einen Scheme-Dialekt entwickelt, der ein Schleifenkonstrukt enthält.
%Vor% mit der offensichtlichen Semantik. Wenn Sie nun das naive eval
aufrufen, um eine Schleife zu evaluieren, die zehnmal ausgeführt wird
Dann wird der Schleifenkörper zehn Mal analysiert. Mit der Version von eval
, die die Analyse von der Auswertung trennt, ist der Schleifenkörper einmal analyze
d und dann zehnmal ausgewertet.
Die Analysephase gibt eine Prozedur zurück, die in Ihrem Scheme-Interpreter schnell sein kann. Es könnte jedoch denkbar sein, alle Arten von Optimierungen vorzunehmen (Dead-Code-Analyse, JIT-Kompilierung ), um Code zu bearbeiten .).
larsmans 'Antworten sind sehr gut.
Als eine ergänzende Antwort kann man analyze(environ)
auch als eine Curry-Form von eval(expr, environ)
betrachten, in der der Parameter expr
im Voraus übergeben wurde. In SICP können Sie den Beispielcode lesen:
Wenn Sie let (([var] [preprocessed stuff]))
sehen, wird die Vorverarbeitung in einem Abschluss gespeichert, bis sie später benötigt wird, wenn environ
übergeben wird.