Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #lang racket
- (require racket/mpair)
- ;;;;;;;;;;
- ;; 4.11 ;;
- ;;;;;;;;;;
- ;; A frame is a list of name-value pairs with a 'frame header. Note that we can't
- ;; use the empty list for the empty frame; otherwise, the empty frame would have no
- ;; mcar or mcdr to mutate when we want to add a binding. Hence the 'frame header.
- (define the-empty-frame (mlist 'frame))
- (define (empty-frame? frame)
- (empty? (frame-bindings frame)))
- (define (make-frame vars vals)
- (mcons 'frame (mmap mcons vars vals)))
- (define (frame-bindings frame) (mcdr frame))
- (define (frame-variables frame) (mmap mcar (frame-bindings frame)))
- (define (frame-values frame) (mmap mcdr (frame-bindings frame)))
- (define (binding-variable binding) (mcar binding))
- (define (binding-value binding) (mcdr binding))
- (define (set-value! binding val) (set-mcdr! binding val))
- (define (add-binding-to-frame! var val frame)
- (mappend! frame (mlist (mcons var val))))
- ;; TESTS FOR ADD-BINDING-TO-FRAME!
- (define frame1 (make-frame (mlist 'a) (mlist 1)))
- (add-binding-to-frame! 'b 2 frame1)
- ;; (mcons 'frame (mcons (mcons 'a 1) (mcons (mcons 'b 2) '())))
- (equal? frame1 (make-frame (mlist 'a 'b) (mlist 1 2)))
- ;; #t
- (define frame2 the-empty-frame)
- (add-binding-to-frame! 'a 1 frame2)
- ;; (mcons 'frame (mcons (mcons 'a 1) '()))
- (equal? frame2 (make-frame (mlist 'a) (mlist 1)))
- ;; #t
- ;; We also need to change set-variable-value! and define-variable!.
- ;; lookup-variable-value is OK as written for the new frame representation.
- (define (find-binding-in-frame var frame)
- ; Return the var-val pair if present else false.
- (define (loop bindings)
- (cond [(empty? bindings) false]
- [else
- (define b (mcar bindings))
- (if (eq? var (binding-variable b))
- b
- (loop (mcdr bindings)))]))
- (loop (frame-bindings frame)))
- (define (set-variable-value! var val env)
- (define (env-loop env)
- (cond [(eq? env the-empty-environment)
- (error "Unbound variable -- SET!" var)]
- [else
- (define frame (first-frame env))
- (define b (find-binding-in-frame var frame))
- (if b
- (set-value! b val)
- (env-loop (enclosing-environment env)))]))
- (env-loop env))
- (define (define-variable! var val env)
- (define frame (first-frame env))
- (define b (find-binding-in-frame var frame))
- (if b
- (set-mcar! b val)
- (add-binding-to-frame! var val frame)))
- (define (lookup-variable-value var env) ; this is the book version
- (define (env-loop env)
- (define (scan vars vals)
- (cond [(empty? vars)
- (env-loop (enclosing-environment env))]
- [(eq? var (mcar vars))
- (mcar vals)]
- [else (scan (mcdr vars) (mcdr vals))]))
- (cond [(eq? env the-empty-environment)
- (error "Unbound variable" var)]
- [else
- (define frame (first-frame env))
- (scan (frame-variables frame)
- (frame-values frame))]))
- (env-loop env))
- (define (enclosing-environment env) (mcdr env))
- (define (first-frame env) (mcar env))
- (define the-empty-environment empty)
- ;; TEST
- (define f (make-frame (mlist 'a 'b 'c) (mlist 1 2 3)))
- (define env (mlist f))
- (lookup-variable-value 'c env)
- ;; 3
- (set-variable-value! 'c 42 env)
- (lookup-variable-value 'c env)
- ;; 42
- (define-variable! 'd 5 env) ; mappend! in add-binding-to-frame! returns the frame
- ;; (mcons
- ;; 'frame
- ;; (mcons
- ;; (mcons 'a 1)
- ;; (mcons (mcons 'b 2) (mcons (mcons 'c 42) (mcons (mcons 'd 5) '())))))
- (lookup-variable-value 'd env)
- ;; 5
- env
- ;; (mcons
- ;; (mcons
- ;; 'frame
- ;; (mcons
- ;; (mcons 'a 1)
- ;; (mcons (mcons 'b 2) (mcons (mcons 'c 42) (mcons (mcons 'd 5) '())))))
- ;; '())
- ;;;;;;;;;;
- ;; 4.12 ;;
- ;;;;;;;;;;
- ;; We can abstract these procedures in terms of find-binding-in-frame and a new
- ;; procedure find-binding-in-env. We'll keep the frame representation from exercise
- ;; 4.11.
- (define (find-binding-in-env var env)
- ; Return the closest binding for var if present else false.
- (cond [(eq? env the-empty-environment) false]
- [else
- (define b (find-binding-in-frame var (first-frame env)))
- (or b (find-binding-in-env var (enclosing-environment env)))]))
- (define (lookup-variable-value2 var env)
- (define b (find-binding-in-env var env))
- (if b
- (binding-value b)
- (error "Unbound variable" var)))
- (define (set-variable-value2! var val env)
- (define b (find-binding-in-env var env))
- (if b
- (set-value! b val)
- (error "Unbound variable -- SET!" var)))
- (define (define-variable2! var val env)
- (define frame (first-frame env))
- (define b (find-binding-in-frame var frame))
- (if b
- (set-value! b val)
- (add-binding-to-frame! var val frame)))
- ;; TEST
- (define f2 (make-frame (mlist 'x 'y 'z) (mlist 10 11 12)))
- (define env2 (mlist f2))
- (lookup-variable-value2 'y env2)
- ;; 11
- (set-variable-value2! 'y 42 env2)
- (lookup-variable-value2 'y env2)
- ;; 42
- (define-variable2! 'w 33 env2) ; mappend! in add-binding-to-frame! returns the frame
- ;; (mcons
- ;; 'frame
- ;; (mcons
- ;; (mcons 'x 10)
- ;; (mcons (mcons 'y 42) (mcons (mcons 'z 12) (mcons (mcons 'w 33) '())))))
- (lookup-variable-value2 'w env2)
- ;; 33
- env2
- ;; (mcons
- ;; (mcons
- ;; 'frame
- ;; (mcons
- ;; (mcons 'x 10)
- ;; (mcons (mcons 'y 42) (mcons (mcons 'z 12) (mcons (mcons 'w 33) '())))))
- ;; '())
- ;; (lookup-variable-value2 'not-there env2)
- ;; ;; . . Unbound variable not-there
- ;;;;;;;;;;
- ;; 4.13 ;;
- ;;;;;;;;;;
- ;; We'll have make-unbound! remove only the binding in the first frame. In the
- ;; interest of modularity, it seems unwise to have a procedure that searches and
- ;; modifies the entire environment, possibly removing bindings created by other parts
- ;; of the program. But if we ever needed that functionality, we could always create
- ;; a make-global-unbound! function.
- ;; Should we remove frames from the environment that have had all the bindings
- ;; removed? I think not because the frame is a node in a graph, a place, and not
- ;; just a collection of values. A procedure may want to create new bindings in that
- ;; place without realizing it is empty and needs to be created. So be aware that
- ;; this implementation of make-unbound! may leave empty frames in the environment.
- ;; Use the representaion and helper functions from exercises 4.11 and 4.12.
- (define (remove-binding! b frame)
- ; Mutate frame to remove b. Assume b is in frame.
- (define (loop! lst)
- (if (equal? b (mcar (mcdr lst)))
- (set-mcdr! lst (mcdr (mcdr lst)))
- (loop! (mcdr lst))))
- (loop! frame))
- (define (make-unbound! var env)
- (define frame (first-frame env))
- (define b (find-binding-in-frame var frame))
- (when b (remove-binding! b frame)))
- ;; TEST
- (define f3 (make-frame (mlist 'p 'q 'r) (mlist 1 2 3)))
- (define env3 (mlist f3))
- env3
- ;; (mcons
- ;; (mcons
- ;; 'frame
- ;; (mcons (mcons 'p 1) (mcons (mcons 'q 2) (mcons (mcons 'r 3) '()))))
- ;; '())
- (make-unbound! 'p env3)
- env3
- ;; (mcons (mcons 'frame (mcons (mcons 'q 2) (mcons (mcons 'r 3) '()))) '())
- (make-unbound! 'r env3)
- env3
- ;; (mcons (mcons 'frame (mcons (mcons 'q 2) '())) '())
- (make-unbound! 'q env3)
- env3
- ;; (mcons (mcons 'frame '()) '())
- (eq? env3 the-empty-environment)
- ;; #f
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement