Exercise 4.4. Recall the definitions of the special forms and and or from chapter 1: * and: The expressions are evaluated from left to right. If any expression evaluates to false, false is returned; any remaining expressions are not evaluated. If all the expressions evaluate to true values, the value of the last expression is returned. If there are no expressions then true is returned. * or: The expressions are evaluated from left to right. If any expression evaluates to a true value, that value is returned; any remaining expressions are not evaluated. If all expressions evaluate to false, or if there are no expressions, then false is returned. Install and and or as new special forms for the evaluator by defining appropriate syntax procedures and evaluation procedures eval-and and eval-or. Alternatively, show how to implement and and or as derived expressions. ———————————————————————————————————————————————————————————————————————— We can implement and and or as derived expressions implemented in terms of if. In eval we can handle these analogously to cond expressions: (define (eval exp env) (cond ... ... ((and? exp) (eval (and->if exp) env)) ((or? exp) (eval (or->if exp) env)) ...)) (define (and? exp) (tagged-list? exp 'and)) (define (if? exp) (tagged-list? exp 'and)) ;; selectors for the abstract syntax (define (and-exprs exp) (cdr exp)) (define (or-exprs exp) (cdr exp)) ;; despite the procedure name, we only return an if expression for ;; and and or expressions with more than one sub-expression. (define (and->if exp) (expand-and (and-exprs exp))) (define (expand-and exprs) (if (null? exprs) ;; if empty, return 'true 'true (let ((first (car exprs)) (rest (cdr exprs))) (if (null? rest) ;; with only one subexpression, (and expr) -> expr first (make-if first (expand-and rest) 'false))))) (define (or->if exp) (expand-or (or-exprs exp))) (define (expand-or exprs) (if (null? exprs) 'false (let ((first (car exprs)) (rest (cdr exprs))) (if (null? rest) first (make-if first first ;; XXX see below (or->if rest)))))) This implementation of and->if is fine, but the implementation of or->if is fundamentally flawed, because it can evaluate an expression twice if that expression is true. There are several ways around this. One is to implement or directly and not as a derived expression. Another is to generate for (or a b) an expression which uses the let special form to store the value of a, and then tests this value using if, and either returns it, or continues on into b. Unfortunately, there is no way to implement that cleanly here with what we have so far, as to introduce a binding into the expression would require us to choose a name for that binding, and any name so chosen could potentially conflict with a name already existing in the expression. Here instead we implement or as a special form: (define (eval exp env) (cond ... ... ((and? exp) (eval (and->if exp) env)) ((or? exp) (eval-or exp env)) ...)) (define (eval-or exp env) (if (null? (or-exprs exp)) false (let ((first-value (eval (car (or-exprs exp)) env))) (if (true? first-value) first-value (eval-or (cdr (or-exprs exp)) env)))))