<< Previous exercise (3.82) | Index | Next exercise (4.2) >>
99% certain meteorgan's code should have the words "left" and "right" swapped in the let* in list-of-values2 (the right to left version). Only 99%, so I won't presume to edit it myself.
So some time after craig wrote this, someone else came and edited the code. But they apparently didn't think to leave any kind of comment letting us know they made the swap. From the edit history:
;; the original code by meteorgan (let* ((left (eval (rest-operands exps) env)) (right (eval (first-operand exps) env))) (cons left right)) ;; the current corrected code (let* ((right (eval (rest-operands exps) env)) (left (eval (first-operand exps) env))) (cons left right))
The main thing to understand is the difference between let and let*, namely in how they handle the order they evaluate expressions and assign values.
In a regular let, all expressions are evaluated first, and then the variables are assigned their values. The order the variables get their values isn’t fixed or predictable, because they're all evaluated and assigned more or less at the same time.
In a let*, on the other hand, variables are assigned one after the other, in the exact order they appear. The first variable is evaluated and assigned its value first, then the second, and so on. This means the evaluation order is determined by the sequence in which you write the variable assignments.
So in the corrected code above, right is evaluated and assigned its value first by recursively evaluating rest-operands. Only after that is left evaluated and assigned its value.
(define (list-of-values-lr exps env) (if (no-operands? exps) '() (let ((first (eval (first-operand exps) env))) (let ((rest (list-of-values-lr (rest-operands exps) env))) (cons first rest))))) (define (list-of-values-rl exps env) (if (no-operands? exps) '() (let ((rest (list-of-values-rl (rest-operands exps) env))) (let ((first (eval (first-operand exps) env))) (cons first rest)))))
;;; left-to-right (define (list-of-values-l2r exps env) (if (no-operands? exps) '() (let ((first-exp (eval (first-operand exps) env))) (cons first-exp (list-of-values-l2r (rest-operands exps) env))))) ;;; right-to-left (define (list-of-values-r2l exps env) (list-of-values-l2r (reverse exps) env))
; left-to-right (define (list-of-values exps env) (let ((first '()) (rest '())) (if (no-operands? exps) rest (begin (set! first (eval (first-operand exps) env)) (set! rest (list-of-values (rest-operands exps) env)) (cons first rest))))) ; right-to-left (define (list-of-values exps env) (let ((first '()) (rest '())) (if (no-operands? exps) rest (begin (set! rest (list-of-values (rest-operands exps) env)) (set! first (eval (first-operand exps) env)) (cons first rest)))))
I think it's wrong to use cons in right to left.
;; ex 4.1 ;; left to right (define (list-of-values-l2r exps env) (if (no-operands? exps) '() (let ((v (eval (first-operand exps) env))) (cons v (list-of-values-l2r (cdr exps) env))))) ;; right to left (define (list-of-values-r2l exps env) (reverse (list-of-values-l2r (reverse exps) env)))
Why? Only the order of evaluation matters. Once the expressions have been evaluated, it literally doesn't matter in what order cons evaluates its arguments because they have already been evaluated and thus evaluating from left-to-right and right-to-left are equivalent. Right?
;; left-to-right (define (list-of-values exps env) (if (no-operands? exps) '() (let ((first (eval (first-operand exps) env))) (cons first (list-of-values (rest-operands exps) env))))) ;; right-to-left (define (list-of-values exps env) (if (no-operands? exps) '() (let ((rest (list-of-values (rest-operands exps) env))) (cons (eval (first-operand exps) env) rest))))
meteorgan