Exercise 5.32. Using the preserving mechanism, the compiler will avoid saving and restoring env around the evaluation of the operator of a combination in the case where the operator is a symbol. We could also build such optimizations into the evaluator. Indeed, the explicit-control evaluator of section 5.4 already performs a similar optimization, by treating combinations with no operands as a special case. a. Extend the explicit-control evaluator to recognize as a separate class of expressions combinations whose operator is a symbol, and to take advantage of this fact in evaluating such expressions. ———————————————————————————————————————————————————————————————————————— We can add a primitive test that tests that a combination begins with a symbol, and then if so, simply look up the symbol directly as a variable, and then jump to the normal procedure application path. Changes to eceval.scm: --- ch5-eceval.scm 2010-04-22 11:08:28.000000000 -0600 +++ 5.32-eceval.scm 2010-05-09 15:45:04.000000000 -0600 @@ -61,6 +61,7 @@ (list 'true? true?) (list 'make-procedure make-procedure) (list 'compound-procedure? compound-procedure?) + (list 'combination-symbol-op? combination-symbol-op?) (list 'procedure-parameters procedure-parameters) (list 'procedure-body procedure-body) (list 'procedure-environment procedure-environment) @@ -133,6 +134,8 @@ (branch (label ev-lambda)) (test (op begin?) (reg exp)) (branch (label ev-begin)) + (test (op combination-symbol-op?) (reg exp)) + (branch (label ev-symbol-combination)) (test (op application?) (reg exp)) (branch (label ev-application)) (goto (label unknown-expression-type)) @@ -153,6 +156,12 @@ (reg unev) (reg exp) (reg env)) (goto (reg continue)) +ev-symbol-combination + (assign unev (op operands) (reg exp)) + (assign exp (op operator) (reg exp)) + (assign exp (op lookup-variable-value) (reg exp) (reg env)) + (goto (label ev-appl-do-argl)) + ev-application (save continue) (save env) @@ -164,6 +173,7 @@ ev-appl-did-operator (restore unev) (restore env) +ev-appl-do-argl (assign argl (op empty-arglist)) (assign proc (reg val)) (test (op no-operands?) (reg unev)) eceval-support.scm also needs to define combination-symbol-op?. I did not test this. ———————————————————————————————————————————————————————————————————————— b. Alyssa P. Hacker suggests that by extending the evaluator to recognize more and more special cases we could incorporate all the compiler's optimizations, and that this would eliminate the advantage of compilation altogether. What do you think of this idea? ———————————————————————————————————————————————————————————————————————— This is true, but the compiler will take some time to run. If you do all the optimizations every time you run the program, then the runtime includes the time to perform the optimizations. The benefit of a separate compilation step is that you can run it once, save the compiled result, and then run that compiled programs many times, benefiting from the optimizations which only needed to be performed once. However, there are optimizations that can only be performed at runtime, so a theoretically optimal approach would be a hybrid interpreter that can compile, save compiled results between runs, interpret code with full generality, and produce specialized machine code for optimum speed. The only disadvantage to such approaches is the increased complexity of building and maintaining them.