170 likes | 258 Views
Implementing Recursion. let insufficient for recursion. (let ( ( fact ( lambda (n) (if (zero? n) 1 ( * n ( fact (- n 1)) ) ) ) ) ) (fact 6) )
E N D
Implementing Recursion L10Rec
let insufficient for recursion (let((fact(lambda (n) (if (zero? n) 1 (* n (fact (- n 1))) ) ) )) (fact 6) ) • Problem : The closure to be formed must freeze the environment containing the “correct” binding for fact which is the closure itself! L10Rec
“Correct” Environment : Circularity Old approach L10Rec
Concrete syntax <expression> ::= letrec <procdecl>* in <expression> <idlist> ::= () | (<identifier> {, <identifier>}*) <procdecl> ::= <identifier> <idlist> = <expression> • Abstract Syntax letrec-exp (proc-names idss bodies letrec-body) L10Rec
Example letrec even(x) = if zero?(x) then 1 else (odd sub1(x)) odd(x) = if zero?(x) then 0 else (even sub1(x)) in (odd 13); L10Rec
Potential Approaches • Delay the creation of the closure until a suitable environment has been formed. • Create the environment and then change what one of its procedure binding points to. • Simulate letrec using let and assignment in the object language, or alternatively, using assignment in the meta-language. • Create the closure with a “hole” and then “install” the final environment, when available, later. • Refer to EOPL (1st Ed., Chapter 7) L10Rec
Delayed Closure Creation (New Env.) • So far, each procedure definition is translated as a closure (< formals, body, creation-time-environment >), and the environment binds a closure to proc name. • To handle (mutually) recursive procedures, the creation of the closure is delayed until the binding for the proc name is required (using apply-env), prior to carrying out apply-proc. L10Rec
(define eval-expression (lambda (exp env) (cases expression exp ... (primapp-exp (prim rands) … ) (app-exp (rator rands) (let ((proc (eval-expression ratorenv)) (args (eval-rands randsenv))) (if (procval? proc) (apply-procval proc args) (eopl:error 'eval-expression “Applying non-procedure ~s" proc)) (proc-exp (ids body) (closure idsbody env)) (let-exp (ids rands body) (let ((args (eval-rands rands env))) (eval-expression body (extend-env ids args env)))) (letrec-exp (proc-names idss bodies letrec-body) (eval-expression letrec-body (extend-env-recursively proc-namesidssbodies env))) ))) L10Rec
Extended Recursive Environments (define-datatype environment environment? (empty-env-record) (extended-env-record (syms (list-of symbol?)) (vals vector?) (env environment?)) (recursively-extended-env-record (proc-names (list-of symbol?)) (idss (list-of (list-of symbol?))) (bodies (list-of expression?)) (env environment?)) ) L10Rec
(cont’d) (define (empty-env) (empty-env-record)) (define (extend-env syms vals env) (extended-env-record syms (list->vector vals) env)) (define (extend-env-recursively proc-names idss bodies old-env) (recursively-extended-env-record proc-names idss bodies old-env)) L10Rec
(cont’d) (define (apply-env env sym) (cases environment env (empty-env-record () (eopl:error 'empty-env “Unbound ~s" sym)) (extended-env-record (syms vals old-env) (let ((pos (rib-find-position sym syms))) (if (number? pos) (vector-ref vals pos) (apply-env old-env sym)))) (recursively-extended-env-record (proc-names idss bodies old-env) (let ((pos (rib-find-position sym proc-names))) (if (number? pos) (closure (list-ref idss pos) (list-ref bodies pos) env) (apply-env old-env sym)))) )) L10Rec
let x = 6 in let y = 8 in letrec p = D1 q = D2 in let z = 10 in (p) INIT-ENV : [] EXT-ENV: [x = 6,[]] EXT-ENV: [y=8, [x = 6,[]]] REC-EXT-ENV: [{p=D1,q=D2}+[y=8, [x = 6,[]]] EXT-ENV: [z=10, [{p=D1,q=D2}+[y=8, [x = 6,[]]] ] Example Closure: D1%[{p=D1,q=D2}+[y=8, [x = 6,[]]] L10Rec
Another Implementation (based on procedures) (define environment? procedure?) (define (apply-env env sym) (env sym)) (define (empty-env) (lambda (sym) (eopl:error 'empty-env “Unbound ~s" sym))) (define (extend-env ids vals env) (lambda (sym) (let ((pos (rib-find-position sym ids))) (if (number? pos) (list-ref vals pos) (apply-env env sym))) )) L10Rec
(cont’d) (define (extend-env-recursively proc-names idss bodies old-env) (letrec ((rec-env (lambda (sym) (let((pos(rib-find-position sym proc-names))) (if (number? pos) (closure (list-ref idss pos) (list-ref bodies pos) rec-env) (apply-env old-env sym)))) )) rec-env) ) L10Rec
Simulating letrec using let and assignment (letrec ((var1 exp1) (var2 exp2)) exp ) (* exp1 and exp2 are lambda-forms *) (let ((var1 ’*) (var2 ’*)) (set! var1 exp1) (set! var2 exp2) exp ) L10Rec
Yet Another Implementation (based on assignment) (define (extend-env-recursively proc-names idss bodies old-env) (let ((len (length proc-names))) (let ((vec (make-vector len))) (let ((env (extended-env-record proc-names vec old-env))) (for-each (lambda (pos ids body) (vector-set! vec pos (closure ids body env))) (iota len) idss bodies) env)))) L10Rec
(cont’d) (define apply-env (lambda (env sym) (cases environment env (empty-env-record () (eopl:error 'empty-env “Unbound ~s" sym)) (extended-env-record (syms vals old-env) (let ((pos (rib-find-position sym syms))) (if (number? pos) (vector-ref vals pos) (apply-env old-env sym)))) ))) L10Rec