<< Previous exercise (4.19) | Index | Next exercise (4.21) >>


 ;; a 
 ;; letrec expression 
 (define (letrec? expr) (tagged-list? expr 'letrec)) 
 (define (letrec-inits expr) (cadr expr)) 
 (define (letrec-body expr) (cddr expr)) 
 (define (declare-variables expr) 
         (map (lambda (x) (list (car x) '*unassigned*)) (letrec-inits expr))) 
 (define (set-variables expr) 
         (map (lambda (x) (list 'set! (car x) (cadr x))) (letrec-inits expr))) 
 (define (letrec->let expr) 
         (list 'let (declare-variables expr)  
                (make-begin (append (set-variables expr) (letrec-body expr))))) 


 ;; The lambda in `let' is evaluated in the context of the enclosing environment, 
 ;; in which the bindings of the lambda itself are not in place. 
 ;; The trick of encoding `letrec' is that we first establish the bindings, and 
 ;; then define the lambdas in an environment where the bindings are there, so 
 ;; the recursive call can succeed. 
 ;; The following snippets illustrate the difference: 
 (let ((fact <fact-body>)) 
 ;; is encoded by 
 ((lambda (fact) 
 ;; such that `<fact-body>' can't refer to `fact'. While: 
 (letrec ((fact <fact-body>)) 
 ;; is encoded by 
 ((lambda (fact) 
    (set! fact <fact-body>) 
 ;; note that in the context of `<fact-body>', the variable `fact' itself is 
 ;; bound.