4.23 Exercise 4.23
Consider the two implementations of analyze-sequence:
(define (analyze-sequence exps) (define (sequentially proc1 proc2) (lambda (env) (proc1 env) (proc2 env))) (define (loop first-proc rest-procs) (if (null? rest-procs) first-proc (loop (sequentially first-proc (car rest-procs)) (cdr rest-procs)))) (let ((procs (map analyze exps))) (if (null? procs) (error "Empty sequence -- ANALYZE")) (loop (car procs) (cdr procs))))
(define (analyze-sequence exps) (define (execute-sequence procs env) (cond ((null? (cdr procs)) ((car procs) env)) (else ((car procs) env) (execute-sequence (cdr procs) env)))) (let ((procs (map analyze exps))) (if (null? procs) (error "Empty sequence -- ANALYZE") (lambda (env) (execute-sequence procs env)))))
Suppose we have a body with a single expression. In the first variant, loop will immediately hit the base case and return that (analyzed) expression, to be executed directly when being evaluated. In the second variant, the base case in execute-sequence will similarly immediately be hit when the body is evaluated. The key difference is that this happens during execution and not analysis, as the text says. In the presence of repeated execution, this is a bit suboptimal.
In the case where the body has two expressions, the second variant will still defer all work in the analysis phase. During evaluation, it will iterate through thef sequence of expressions, evaluating each in turn. The first variant, however, will construct a new procedure that, when given an environment, will first call the first expression and then the second. (In the general case, it will chain these together, by using the sequenced execution of the first two expressions as the new "first expression" in the next loop iteration.) Again, the second variant is suboptimal when a procedure is evaluated more than once.
Another thing to note is that, by doing a list traversal at evaluation time, the second variant also has to repeatedly check for the end of the list. This is not the case when evaluating the sequence in the first variant, as the new composed procedure calls exactly what it needs to unconditionally.