Structure and Interpretation of Computer Programs



Yüklə 2,71 Mb.
Pdf görüntüsü
səhifə202/222
tarix08.08.2018
ölçüsü2,71 Mb.
#61085
1   ...   198   199   200   201   202   203   204   205   ...   222

a. Exercise 5.27 asked you to determine, as a function of n, the number of pushes and the maximum

stack depth needed by the evaluator to compute n! using the recursive factorial procedure given above. 

Exercise 5.14 asked you to do the same measurements for the special-purpose factorial machine shown

in figure 5.11. Now perform the same analysis using the compiled 

factorial

 procedure.

Take the ratio of the number of pushes in the compiled version to the number of pushes in the

interpreted version, and do the same for the maximum stack depth. Since the number of operations and

the stack depth used to compute n! are linear in n, these ratios should approach constants as n becomes

large. What are these constants? Similarly, find the ratios of the stack usage in the special-purpose

machine to the usage in the interpreted version.

Compare the ratios for special-purpose versus interpreted code to the ratios for compiled versus

interpreted code. You should find that the special-purpose machine does much better than the

compiled code, since the hand-tailored controller code should be much better than what is produced by

our rudimentary general-purpose compiler.

b. Can you suggest improvements to the compiler that would help it generate code that would come

closer in performance to the hand-tailored version? 

Exercise 5.46.  Carry out an analysis like the one in exercise 5.45 to determine the effectiveness of

compiling the tree-recursive Fibonacci procedure

(define (fib n)

  (if (< n 2)

      n

      (+ (fib (- n 1)) (fib (- n 2)))))



compared to the effectiveness of using the special-purpose Fibonacci machine of figure 5.12. (For

measurement of the interpreted performance, see exercise 5.29.) For Fibonacci, the time resource used

is not linear in n; hence the ratios of stack operations will not approach a limiting value that is

independent of n



Exercise 5.47.  This section described how to modify the explicit-control evaluator so that interpreted

code can call compiled procedures. Show how to modify the compiler so that compiled procedures can

call not only primitive procedures and compiled procedures, but interpreted procedures as well. This

requires modifying 

compile-procedure-call

 to handle the case of compound (interpreted)

procedures. Be sure to handle all the same 

target


 and 

linkage


 combinations as in 

compile-proc-appl

. To do the actual procedure application, the code needs to jump to the

evaluator’s 

compound-apply

 entry point. This label cannot be directly referenced in object code

(since the assembler requires that all labels referenced by the code it is assembling be defined there),

so we will add a register called 

compapp

 to the evaluator machine to hold this entry point, and add an



instruction to initialize it: 

  (assign compapp (label compound-apply))

  (branch (label external-entry))      ; branches if flag is set

read-eval-print-loop

  ...

To test your code, start by defining a procedure 



f

 that calls a procedure 

g

. Use 


compile-and-go

 to


compile the definition of 

f

 and start the evaluator. Now, typing at the evaluator, define 



g

 and try to

call 

f




Exercise 5.48.  The 

compile-and-go

 interface implemented in this section is awkward, since the

compiler can be called only once (when the evaluator machine is started). Augment the

compiler-interpreter interface by providing a 

compile-and-run

 primitive that can be called from

within the explicit-control evaluator as follows:



;;; EC-Eval input:

(compile-and-run

 ’(define (factorial n)

    (if (= n 1)

        1

        (* (factorial (- n 1)) n))))



;;; EC-Eval value:

ok

;;; EC-Eval input:

(factorial 5)



;;; EC-Eval value:

120

Exercise 5.49.  As an alternative to using the explicit-control evaluator’s read-eval-print loop, design a

register machine that performs a read-compile-execute-print loop. That is, the machine should run a

loop that reads an expression, compiles it, assembles and executes the resulting code, and prints the

result. This is easy to run in our simulated setup, since we can arrange to call the procedures 

compile

 and 


assemble

 as ‘‘register-machine operations.’’ 



Exercise 5.50.  Use the compiler to compile the metacircular evaluator of section 4.1 and run this

program using the register-machine simulator. (To compile more than one definition at a time, you can

package the definitions in a 

begin


.) The resulting interpreter will run very slowly because of the

multiple levels of interpretation, but getting all the details to work is an instructive exercise. 



Exercise 5.51.  Develop a rudimentary implementation of Scheme in C (or some other low-level

language of your choice) by translating the explicit-control evaluator of section 5.4 into C. In order to

run this code you will need to also provide appropriate storage-allocation routines and other run-time

support. 



Exercise 5.52.  As a counterpoint to exercise 5.51, modify the compiler so that it compiles Scheme

procedures into sequences of C instructions. Compile the metacircular evaluator of section 4.1 to

produce a Scheme interpreter written in C. 

33

 This is a theoretical statement. We are not claiming that the evaluator’s data paths are a particularly



convenient or efficient set of data paths for a general-purpose computer. For example, they are not

very good for implementing high-performance floating-point calculations or calculations that

intensively manipulate bit vectors. 

34

 Actually, the machine that runs compiled code can be simpler than the interpreter machine, because



we won’t use the 

exp


 and 

unev


 registers. The interpreter used these to hold pieces of unevaluated

expressions. With the compiler, however, these expressions get built into the compiled code that the

register machine will run. For the same reason, we don’t need the machine operations that deal with

expression syntax. But compiled code will use a few additional machine operations (to represent

compiled procedure objects) that didn’t appear in the explicit-control evaluator machine. 



Yüklə 2,71 Mb.

Dostları ilə paylaş:
1   ...   198   199   200   201   202   203   204   205   ...   222




Verilənlər bazası müəlliflik hüququ ilə müdafiə olunur ©genderi.org 2024
rəhbərliyinə müraciət

    Ana səhifə