200 likes | 271 Views
Implementing Recursion. Recap: Goals. Experimenting with PL design alternatives Scoping, parameter passing, arrays, ... Runnable prototype of reference implementation. Cf. Informal description of a programming problem with an example executable for reference. Recap: closure.
E N D
Recap: Goals • Experimenting with PL design alternatives Scoping, parameter passing, arrays, ... • Runnable prototype of reference implementation. • Cf. Informal description of a programming problem with an example executable for reference.
Recap: closure • procedure definition • bind a closure to proc name. • closure == (formals, body, creation-environment)
let insufficient for recursion • Consider, in the defined language (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!
“Correct” Environment : Circularity Old approach
Adding LETREC • Concrete syntax <expression> ::=letrec {<procdecl>}* in <expression> <idlist> ::= () | (<identifier> {, <identifier>}*) <procdecl> ::= <identifier> <idlist> = <expression> • Abstract Syntaxletrec-exp (proc-names idss bodies letrec-body)
letrec 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);
Three 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)
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.
(define eval-expression (lambda (exp env) (cases expression exp … (primapp-exp (prim rands) … ) (app-exp (ratorrands) (let ((proc (eval-expression ratorenv)) (args (eval-randsrandsenv))) (if (procval? proc) (apply-procval proc args) (eopl:error 'eval-expression “Applying non-procedure ~s" proc)) (proc-exp (ids body) (closure idsbodyenv)) (let-exp (ids rands body) (let ((args (eval-randsrandsenv))) (eval-expression body (extend-envidsargsenv)))) (letrec-exp (proc-names idss bodies letrec-body) (eval-expression letrec-body (extend-env-recursively proc-namesidssbodiesenv))) )))
Extended recursive environment TBD • A list of the variables in the procdecls that is created by mapping the selector function for variables from a procdecl onto the list of procedure declarations. • A vector containing procedure records (not closures) created from the formals and body of each procedure in the procedure declarations. • The active environment.
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?)) )
(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))
(cont’d) (define (apply-envenv sym) (cases environment env (empty-env-record () (eopl:error 'empty-env “Unbound ~s" sym)) (extended-env-record (symsvals 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)))) ))
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
Another Implementation (based on procedures) (define environment? procedure?) (define (apply-envenv sym) (env sym)) (define (empty-env) (lambda (sym) (eopl:error 'empty-env “Unbound ~s" sym))) (define (extend-env ids valsenv) (lambda (sym) (let ((pos (rib-find-position sym ids))) (if (number? pos) (list-ref vals pos) (apply-envenv sym))) ))
(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) )
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 )
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))))
(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)))) )))