diff --git a/mceval/lazy-list.rkt b/mceval/lazy-list.rkt index 7a525f7..6332402 100644 --- a/mceval/lazy-list.rkt +++ b/mceval/lazy-list.rkt @@ -6,8 +6,21 @@ (#%provide lazy-lists-program) (define lazy-lists-program - '((define (cons x y) - (lambda (m) (m x y))) + '( + ;; The cons procedure needs to be created once at interpreter + ;; start up. This ensures that it captures only the initial + ;; environment. Then when it is called, that environment is + ;; extended to bind its parameters to the inputs. + ;; Specifically, it doesn't capture the whole environment at + ;; the call site of cons. The latter is what happens if we + ;; create the procedure at the call site. This leads to + ;; excessive memory use. + ;; lazy-lambda-pair produces a specially tagged procedure that is + ;; recognised as a lazy pair by the evaluator but is otherwise + ;; just a normal procedure. + (define cons + (lambda (x y) + (lazy-pair-lambda (m) (m x y)))) (define (car z) (z (lambda (p q) p))) diff --git a/mceval/leval.rkt b/mceval/leval.rkt index eddfe0b..43f2cdc 100644 --- a/mceval/leval.rkt +++ b/mceval/leval.rkt @@ -35,12 +35,13 @@ (make-procedure (lambda-parameters exp) (lambda-body exp) env)) + ((lazy-pair-lambda? exp) + (make-lazy-pair-procedure (lambda-parameters exp) + (lambda-body exp) + env)) ((begin? exp) (eval-sequence (begin-actions exp) env lazy-eval)) ((cond? exp) (lazy-eval (cond->if exp) env)) - ((cons? exp) (eval-cons (cons-first-exp exp) - (cons-second-exp exp) - env)) ((application? exp) ; clause from book (lazy-apply (actual-value (operator exp) env) (operands exp) @@ -57,6 +58,7 @@ procedure (list-of-arg-values arguments env))) ; changed ((lazy-pair? procedure) + ;; Skip over the lazy-pair tag if we have a lazy-pair as a procedure (lazy-apply (lazy-pair-proc procedure) arguments env)) ((compound-procedure? procedure) @@ -95,12 +97,12 @@ (define (user-print object env) - (cond ((compound-procedure? object) + (cond ((lazy-pair? object) (display (print-lazy-pair object env))) + ((compound-procedure? object) (display (list 'compound-procedure (procedure-parameters object) (procedure-body object) '))) - ((lazy-pair? object) (display (print-lazy-pair object env))) (else (display object)))) (define (print-lazy-pair lazy-pair env) diff --git a/mceval/syntax.rkt b/mceval/syntax.rkt index 398c056..e09847f 100644 --- a/mceval/syntax.rkt +++ b/mceval/syntax.rkt @@ -112,6 +112,9 @@ (define (make-lambda parameters body) (cons 'lambda (cons parameters body))) +;; Lazy-pair lambda is used to model lazy pairs +(define (lazy-pair-lambda? exp) (tagged-list? exp 'lazy-pair-lambda)) + ;; if (define (if? exp) (tagged-list? exp 'if)) (define (if-predicate exp) (cadr exp)) @@ -384,6 +387,10 @@ (define (compound-procedure? p) (tagged-list? p 'procedure)) +;; Lazy-pair procedure is a normal procedure tagged with lazy-pair to +;; distinguish lazy pairs from procedures +(define (make-lazy-pair-procedure parameters body env) + (make-lazy-pair (make-procedure parameters body env))) (define (procedure-parameters p) (cadr p)) (define (procedure-body p) (caddr p))