Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #lang racket
- (require racket/mpair
- "4-2-2-lazy-repl-program.rkt")
- ;;;;;;;;;;
- ;; 4.27 ;;
- ;;;;;;;;;;
- ;; The lazy interpreter incremented count once when defining w and once again when
- ;; evaluating w. However, further evaluations of w did not increment count.
- ;; When we define w, my-eval is called on the definition-value, which is not delayed.
- ;; So the set! in the outermost call to id is executed, and count is incremented to
- ;; one. However, the inner call to id, being the argument to a compound procedure,
- ;; is delayed, and the second set! is not executed during the definition of w.
- ;; Evaluating w then forces the delayed inner call to id, and the second set!
- ;; statement is executed, incrementing count to two. Because of memoization, w will
- ;; not increment count again.
- ;; (driver-loop)
- ;; ;;; L-Eval input:
- ;; (define count 0)
- ;; ;;; L-Eval value:
- ;; ok
- ;; ;;; L-Eval input:
- ;; (define (id x) (set! count (+ count 1)) x)
- ;; ;;; L-Eval value:
- ;; ok
- ;; ;;; L-Eval input:
- ;; count
- ;; ;;; L-Eval value:
- ;; 0
- ;; ;;; L-Eval input:
- ;; (define w (id (id 10)))
- ;; ;;; L-Eval value:
- ;; ok
- ;; ;;; L-Eval input:
- ;; count
- ;; ;;; L-Eval value:
- ;; 1
- ;; ;;; L-Eval input:
- ;; w
- ;; ;;; L-Eval value:
- ;; 10
- ;; ;;; L-Eval input:
- ;; count
- ;; ;;; L-Eval value:
- ;; 2
- ;; ;;; L-Eval input:
- ;; w
- ;; ;;; L-Eval value:
- ;; 10
- ;; ;;; L-Eval input:
- ;; count
- ;; ;;; L-Eval value:
- ;; 2
- ;;;;;;;;;;
- ;; 4.28 ;;
- ;;;;;;;;;;
- (define (f g) (g 2)) ; f of g is 'apply g to 2'
- ;; Since g is the argument to a compound procedure, it gets delayed. If we did not
- ;; force procedure operations, we couldn't apply g to 2 in the body. It would be a
- ;; thunk containing a procedure but not itself a procedure.
- ;; WITH MY-EVAL USING ACTUAL-VALUE AS IN THE BOOK:
- ;; [(application? expr) ; the application clause in my-eval
- ;; (my-apply (actual-value (operator expr) env)
- ;; (operands expr)
- ;; env)]
- ;; ;;; L-Eval input:
- ;; (define (f g) (g 2))
- ;; ;;; L-Eval value:
- ;; ok
- ;; ;;; L-Eval input:
- ;; (define (plus3 x) (+ x 3))
- ;; ;;; L-Eval value:
- ;; ok
- ;; ;;; L-Eval input:
- ;; (f plus3)
- ;; ;;; L-Eval value:
- ;; 5
- ;; WITH MY-EVAL CHANGED TO NOT FORCE THE OPERATOR:
- ;; [(application? expr)
- ;; (my-apply (my-eval (operator expr) env)
- ;; (operands expr)
- ;; env)]
- ;; (driver-loop)
- ;; ;;; L-Eval input:
- ;; (define (f g) (g 2))
- ;; ;;; L-Eval value:
- ;; ok
- ;; ;;; L-Eval input:
- ;; (define (plus3 x) (+ x 3))
- ;; ;;; L-Eval value:
- ;; ok
- ;; ;;; L-Eval input:
- ;; (f plus3)
- ;; . . Unknown procedure type -- APPLY {thunk plus3 ...
- ;;;;;;;;;;
- ;; 4.29 ;;
- ;;;;;;;;;;
- ;; Any recursive function with side-effects would run much more slowly without
- ;; memoization than with memoization, when many loops were called for.
- ;; WITH MEMOIZATION:
- ;; Count is incremented once in the evaluation of (square (id 10)).
- ;; (driver-loop)
- ;; ;;; L-Eval input:
- ;; (define count 0)
- ;; ;;; L-Eval value:
- ;; ok
- ;; ;;; L-Eval input:
- ;; (define (id x) (set! count (+ count 1)) x)
- ;; ;;; L-Eval value:
- ;; ok
- ;; ;;; L-Eval input:
- ;; (define (square x) (* x x))
- ;; ;;; L-Eval value:
- ;; ok
- ;; ;;; L-Eval input:
- ;; (square (id 10))
- ;; ;;; L-Eval value:
- ;; 100
- ;; ;;; L-Eval input:
- ;; count
- ;; ;;; L-Eval value:
- ;; 1
- ;; WITHOUT MEMOIZATION:
- ;; To not memoize, use this definition for force-it:
- ;; (define (force-it obj)
- ;; (if (thunk? obj)
- ;; (actual-value (thunk-exp obj) (thunk-env obj))
- ;; obj))
- ;; Count is incremented twice in the evaluation of (square (id 10)).
- ;; (driver-loop)
- ;; ;;; L-Eval input:
- ;; (define count 0)
- ;; ;;; L-Eval value:
- ;; ok
- ;; ;;; L-Eval input:
- ;; (define count 0)
- ;; ;;; L-Eval value:
- ;; ok
- ;; ;;; L-Eval input:
- ;; (define (id x) (set! count (+ count 1)) x)
- ;; ;;; L-Eval value:
- ;; ok
- ;; ;;; L-Eval input:
- ;; (define (square x) (* x x))
- ;; ;;; L-Eval value:
- ;; ok
- ;; ;;; L-Eval input:
- ;; (square (id 10))
- ;; ;;; L-Eval value:
- ;; 100
- ;; ;;; L-Eval input:
- ;; count
- ;; ;;; L-Eval value:
- ;; 2
- ;; (id 10) is evaluated twice in the body of square. Without memoization, this
- ;; triggers two executions of the set!. With memoization, set! is executed only
- ;; once.
- ;;;;;;;;;;
- ;; 4.30 ;;
- ;;;;;;;;;;
- ;;;;;;;;
- ;; A. ;;
- ;;;;;;;;
- ;; Add these to the list of primitive procedures to make the example work:
- ;; (list 'displayln displayln)
- ;; (list 'list list)
- ;; WITH EVAL-SEQUENCE AS IN THE BOOK:
- (my-eval
- '(define (for-each proc items)
- (if (empty? items)
- 'done
- (begin (proc (car items))
- (for-each proc (cdr items)))))
- the-global-environment)
- ;; 'ok
- (my-eval
- '(for-each (lambda (x) (displayln x)) (list 57 321 88))
- the-global-environment)
- ;; 57
- ;; 321
- ;; 88
- ;; 'done
- ;; So the book code does work for this example. This is because the argument x is
- ;; passed to the primitive procedure displayln, and primitive procedures always force
- ;; their arguments.
- ;;;;;;;;
- ;; B. ;;
- ;;;;;;;;
- ;; WITH EVAL-SEQUENCE AS IN THE BOOK:
- (my-eval
- '(define (p1 x)
- (set! x (cons x '(2)))
- x)
- the-global-environment)
- ;; 'ok
- (my-eval
- '(define (p2 x)
- (define (p e)
- e
- x)
- (p (set! x (cons x '(2)))))
- the-global-environment)
- ;; 'ok
- (my-eval '(p1 1) the-global-environment)
- ;; '(1 2)
- (my-eval '(p2 1) the-global-environment)
- ;; (mcons
- ;; 'thunk
- ;; (mcons
- ;; 1
- ;; (mcons ... ; cut out a huge environment list here
- ;; WITH CY D. FECT'S VERSION OF EVAL-SEQUENCE:
- ;; (my-eval
- ;; '(define (p1 x)
- ;; (set! x (cons x '(2)))
- ;; x)
- ;; the-global-environment)
- ;; ;; 'ok
- ;; (my-eval
- ;; '(define (p2 x)
- ;; (define (p e)
- ;; e
- ;; x)
- ;; (p (set! x (cons x '(2)))))
- ;; the-global-environment)
- ;; ;; 'ok
- ;; (my-eval '(p1 1) the-global-environment)
- ;; ;; '(1 2)
- ;; (my-eval '(p2 1) the-global-environment)
- ;; ;; '(1 2)
- ;; So Cy's version 'does the right thing' and forces e in the definition of p2, while
- ;; the book code does not.
- ;;;;;;;;
- ;; C. ;;
- ;;;;;;;;
- ;; The arguments to the lambda in part a. were already forced by the call to the
- ;; primitive procedure display. So pre-forcing them, as Cy wants, would not affect
- ;; the behavior of the for-each.
- ;;;;;;;;
- ;; D. ;;
- ;;;;;;;;
- ;; I can see the utility of Cy's approach. It does the right thing with
- ;; side-effects, but programs using lazy evaluation should not have side-effects
- ;; anyway. I like the book's consistent approach: if we're going to have a lazy
- ;; evaluator, then it should be consistently lazy, and not just lazy except in
- ;; special circumstances.
- ;;;;;;;;;;
- ;; 4.31 ;;
- ;;;;;;;;;;
- ;; The idea is to make all lambda parameters "typed". So now a lambda parameter will
- ;; be a two-element list (<var> <type>) where the type is either 'eager, 'lazy, or
- ;; 'lazy-memo. However, the user does not have to specify the 'eager type when
- ;; defining a procedure. Parameters are 'eager by default.
- ;; The make-all-typed procedure will add the 'eager type to regular arguments when
- ;; constructing a lambda. So make-all-typed will have to be used everywhere that
- ;; make-lambda is used in the program.
- ;; Once all procedure parameters are typed, we need a new procedure selector,
- ;; procedure-types.
- ;; Lastly, we change my-apply to use a new function list-of-args, to replace
- ;; list-of-delayed-args. list-of-args will take an extra parameter, the types of the
- ;; arguments, to construct the appropriate list of values and/or thunks.
- ;; Change define:
- (define (definition? expr)
- (tagged-list? expr 'define))
- (define (procedure-definition? expr)
- (and (definition? expr)
- (not (symbol? (second expr)))))
- (define (definition-variable expr)
- (if (procedure-definition? expr)
- (first (second expr))
- (second expr)))
- (define (definition-parameters expr)
- (rest (second expr)))
- (define (make-all-typed params)
- (map (lambda (p)(if (symbol? p)
- (list p 'eager)
- p))
- params))
- (define (definition-value expr)
- (if (procedure-definition? expr)
- (make-lambda (make-all-typed (definition-parameters expr)) ; changed
- (drop expr 2))
- (third expr)))
- ;; Change make-lambda to type the parameters:
- (define (make-lambda parameters body)
- (cons 'lambda (cons (make-all-typed parameters) body))) ; changed
- ;; Change thunks, force-it, and delay-it:
- (define (thunk-memo? obj) (tagged-list? obj 'thunk-memo))
- (define (thunk-no-memo? obj) (tagged-list? obj 'thunk-no-memo))
- (define (thunk-exp my-thunk) (mcar (mcdr my-thunk)))
- (define (thunk-env my-thunk) (mcar (mcdr (mcdr my-thunk))))
- (define (delay-it-memo expr env)
- (mlist 'thunk-memo expr env))
- (define (delay-it-no-memo expr env)
- (mlist 'thunk-no-memo expr env))
- (define (evaluated-thunk? obj) (tagged-list? obj 'evaluated-thunk))
- (define (thunk-value evaluated-thunk) (mcar (mcdr evaluated-thunk)))
- (define (force-it obj)
- (cond [(thunk-memo? obj)
- (define result (actual-value (thunk-exp obj) (thunk-env obj)))
- (set-mcar! obj 'evaluated-thunk)
- (set-mcar! (mcdr obj) result)
- (set-mcdr! (mcdr obj) empty)
- result]
- [(evaluated-thunk? obj) (thunk-value obj)]
- [(thunk-no-memo? obj)
- (actual-value (thunk-exp obj) (thunk-env obj))]
- [else obj]))
- ;; Change procedure selectors:
- (define (procedure-parameters p) (map first (second p)))
- (define (procedure-types p) (map second (second p)))
- ;; Now we will change list-of-delayed-args to a new function list-of-args. The new
- ;; function list-of-args will take three parameters, arguments, types, and env, and
- ;; produce the appropriate list of values and/or thunks according to the types of the
- ;; procedure arguments.
- (define (list-of-args exprs types env)
- (cond [(no-operands? exprs)
- empty]
- [else
- (define e (first-operand exprs))
- (define t (first types))
- (define arg (cond [(eq? t 'eager) (actual-value e env)]
- [(eq? t 'lazy) (delay-it-no-memo e env)]
- [(eq? t 'lazy-memo) (delay-it-memo e env)]
- [else (error "Unknown type -- LIST-OF-ARGS" t)]))
- (cons arg (list-of-args (rest-operands exprs)
- (rest types)
- env))]))
- ;; Lastly change my-apply to use list-of-args:
- (define (my-apply procedure arguments env)
- (cond [(primitive-procedure? procedure)
- (apply-primitive-procedure
- procedure
- (list-of-arg-values arguments env))]
- [(compound-procedure? procedure)
- (eval-sequence
- (procedure-body procedure)
- (extend-environment
- (procedure-parameters procedure)
- (list-of-args arguments
- (procedure-types procedure)
- env)
- (procedure-environment procedure)))]
- [else (error "Unknown procedure type -- APPLY" procedure)]))
- ;; See "4-2-2-optionally-lazy-repl-program.rkt" for the program with some tests at
- ;; the end of the file.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement