Problem 4: and and or

In the programming language scheme, nearly every piece of syntax forms can be built by macro.

You may find that the behavior of scheme builtin and and or is a little weird. In this problem, we will have a deeper understanding of them by building our version of and and or

For and, we know that:

  • (and) = #t
  • (and 1) = 1
  • (and x1 x2 x3 ... xn) = #f if there is a xi eval to #f, otherwise (and x1 x2 x3 ... xn) eval to xn.

In fact, (and x1 x2 x3 ... xn) can be transformed to this equivalent form:

; (let ...) is not required here
; when implement (or ...), (let ...) wrap is required

(let ((t x1))
  (if t
      (let ((t x2))
        (if t
            (let ((t x3))
                 ...
            #f))
      #f)))

E.g. (and 1 #t 's) is equivalent to

(let ((t 1))
  (if t
      (let ((t #t))
        (if t
            's ; Note: here we don't need to wrap 's with (if ... )
               ; why ?
            #f))
      #f))

So, we can write this macro to transform and expression:

Note:

  1. cs61a scheme doesn't support varargs macro, so we have to put all the operands into a list in our version of and and or. of and (e.g. (and (1 #t 's)))
  2. cs61a scheme doesn't allow shadowing of builtin functions, so we have to use another name for our and (my/and).
(define-macro (my/and operands)
  (cond 
    ((null? operands) #t)
    ((null? (cdr operands)) (car operands))
    (else
      `(let ((t ,(car operands)))
         (if t
             (my/and ,(cdr operands))
             #f)))))

scm> (my/and ())
#t

scm> (my/and (1 2))
2

scm> (my/and (1 #f 3))
#f

Now, use the same technique to implement my/or, (my/or (x1 x2 x3 ...)) should be equivalent to (or x1 x2 x3 ...)