                Massachusetts Institute of Technology
        Department of Electrical Engineering and Computer Science
        6.001 Structure and Interpretation of Computer Programs

                PROBLEM SET 4 SOLUTIONS (Spring 1984)
-------------------------------------------------------------------------
Exercise 2-23: DEEP-REVERSE

For comparison, let's begin with a recursive form of REVERSE:

(define (reverse x)
  (cond ((null? x) nil)
        (else (append (reverse (cdr x))
                      (list (car x))))))

DEEP-REVERSE is almost the same, except for two things: Since (CAR X)
may itself be a list, we must DEEP-REVERSE the CAR before including it
in the APPEND.  Thus, we reduce the problem of DEEP-REVERSE-ing a list
to the problem of DEEP-REVERSE-ing each element of the list.  If we
keep doing this, we eventually get down to the problem of reversing
atoms.  So we need another base case (in addition to NIL), which says
that the reverse of an atom is the atom itself.  Here is the complete
procedure:

(define (deep-reverse x)
  (cond ((null? x) nil)
        ((atom? x) x)
        (else (append (deep-reverse (cdr x))
                      (list (deep-reverse (car x)))))))

Note that the first clause in the COND is technically redundant, since
NIL is an atom.  However, it may be (depending on the context in which
the program will be used) to leave the clause there, since it makes
the algorithm somewhat easier to understand.

Here is the same problem, starting with an iterative REVERSE:

(define (reverse x)
  (define (reverse-iter x reversed-x)
    (if (null? x)
        reversed-x
        (reverse-iter (cdr x)
                      (cons (car x) reversed-x))))
  (reverse-iter x nil))

(define (deep-reverse x)
  (define (deep-reverse-iter x reversed-x)
    (cond ((null? x) reversed-x)
          ((atom? x) x)
          (else (deep-reverse-iter (cdr x)
                                   (cons (deep-reverse (car x))
                                         reversed-x)))))
    (deep-reverse-iter x nil))

Note here that we must test for (null?) before (atom?) because NIL
is an atom.

Now let us define some lists to test and run the procedures.

  (define a '(1 2 3))
  (define b '(4 5))
  (define c (list a (list b 6) 7))

  ==> a
  (1 2 3)

  ==> b
  (4 5)

  ==> c
  ((1 2 3) ((4 5) 6) 7)

  ==> (reverse c)
  (7 ((4 5) 6) (1 2 3))

  ==> (deep-reverse c)
  (7 (6 (5 4)) (3 2 1))

  ==> (reverse (deep-reverse c))
  ((3 2 1) (6 (5 4)) 7)

----------------------------------------------------------------------------

  Exercise 2-24: FRINGE

Here we use a recursive process similar to what was used in
DEEP-REVERSE: We produce the fringe of x by appending the fringe of
the car of x to the fringe of the cdr of x.  This leads us, as above,
to worry about the base cases when x is nil, and when x is atomic.  If
x is nil, we return nil.  If x is an atom, we return the list whose
only element is x (to be accumulated into the append at the next level
up).

(define (fringe x)
  (cond ((null? x) nil)
        ((atom? x) (list x))
        (else  (append (fringe (car x))
                       (fringe (cdr x))))))


  Now try some applications of fringe.

  ==> (fringe c)
  (1 2 3 4 5 6 7)

  ==> (fringe (reverse c))
  (7 4 5 6 1 2 3)

  ==> (fringe (deep-reverse c))
  (7 6 5 4 3 2 1)

----------------------------------------------------------------------------

  Exercise 2-25: MAKE-MOBILE

a. The constructors are make-mobile and make-branch as given in the
notes. The selectors are just the cars and the cadrs of the
CONStructed things.  Cadr is used because we want to extract the first
element of a pair whose second element is nil. We also define
auxiliary compound selectors that return the structure of a branch of
a mobile. This is done only for convenience in writing more complex
procedures for manipulating mobiles.

  (define (make-mobile left right)
    (list left right))

  (define (make-branch length structure)
    (list length structure))

  (define (left-branch mobile)
    (car mobile))

  (define (right-branch mobile)
    (cadr mobile))

  (define (branch-length branch)
    (car branch))

  (define (branch-structure branch)
    (cadr branch))

  (define (left-structure mobile)                       ;auxiliary procedure
    (branch-structure (left-branch mobile)))

  (define (right-structure mobile)                      ;auxiliary procedure
    (branch-structure (right-branch mobile)))

b. Total-weight is similar to fringe and deep-reverse. Comparing with
fringe, we add the total-weight of the left-structure of the mobile to
the total-weight of the right-structure of the mobile at each stage.
At the last stage the left- or right-structure is the weight of a
simple branch or just a number. So testing for number? will terminate
the recursion.

  (define (total-weight mobile)
    (if (number? mobile)
        mobile
        (+ (total-weight (left-structure mobile))
           (total-weight (right-structure mobile)))))     

c. A simple weight is a primitive mobile which must be assumed to be
balanced. A more complex mobile will be balanced if the structures
attached to its left- and right-branches are balanced and if the
torque (which is given by (length)*(total-weight)) of its left-branch
is equal to the torque of its right-branch.

  (define (balanced? mobile)
    (cond ((number? mobile) t)
          (else
           (and (balanced? (left-structure mobile))
                (balanced? (right-structure mobile))
                (= (* (branch-length (left-branch mobile))
                      (total-weight (left-structure mobile)))
                   (* (branch-length (right-branch mobile))
                      (total-weight (right-structure mobile))))))))
  
  We now build some mobiles and test the total-weight and balanced?
  procedures.     

  ==> (define b1 (make-branch 1 2))
  B1

  ==> (define b2 (make-branch 2 1))
  B2

  ==> (define m1 (make-mobile b1 b2))
  M1

  ==> (define b3 (make-branch 1 m1))
  B3

  ==> (define b4 (make-branch 2 m1))
  B4

  ==> (define m2 (make-mobile b3 b4))
  M2

  ==> m1
  ((1 2) (2 1))

  ==> m2
  ((1 ((1 2) (2 1))) (2 ((1 2) (2 1))))

  ==> (total-weight m1)
  3

  ==> (total-weight m2)
  6

  ==> (balanced? m1)
  #!true

  ==> (balanced? m2)
  ()

  d. The only procedures we would have to change are the selectors
  right-branch and branch-structure by replacing cadr by cdr.
  
----------------------------------------------------------------------------

  Exercise 2-26: QUOTE Practice

  ==> (list 'a 'b 'c)
  (A B C)

  ==> (list (list 'george))
  ((GEORGE))

  ==> (cdr '((x1 x2) (y1 y2)))
  ((Y1 Y2))

  ==> (cadr '((x1 x2) (y1 y2)))
  (Y1 Y2)

  ==> (atom? (car '(a short list)))
  #!true

  ==> (memq 'red '((red shoes) (blue socks)))
  ()

  ==> (memq 'red '(red shoes blue socks))
  (RED SHOES BLUE SOCKS)

--------------------------------------------------------------------------


            Laboratory Assignment: A SIMPLE PARTIAL EVALUATOR
                
[Problem 1]

In order to allow PARTIAL-EVAL to manipulate sequences, first we must define
a "sequence" data abstraction similar to the ones defined for if's, cond's,
and so on in the provided code.  A sequence is implemented as a list of the
form:

   (SEQUENCE exp exp ...)

where each "exp" is an arbitrary expression.  Let's call the constructor
MAKE-SEQUENCE and the list-of-expressions selector SEQUENCE-EXPRESSIONS:

(define (make-sequence list-of-expressions)
  (cons 'sequence list-of-expressions))
(define (sequence-expressions sequence)
  (cdr sequence))
(define (sequence? expression)
  (and (list? expression) (eq? (car expression) 'sequence)))

(What's peculiar about these data abstractions is that they rely on the
reader to serve as an implicit constructor.  For example, when you type
(PARTIAL-EVAL '(SEQUENCE (+ X X) (- 3 Y))) the form that's handed to
PARTIAL-EVAL is a well-formed sequence object even though MAKE-SEQUENCE is
never called.  This is in fact an abstraction violation, since if one were
to implement sequences in some other way the reader would now provide the
wrong stuff.  Ideally, one would write a program called a "syntaxer" to take
the structures the reader builds out of the printed forms of the expressions
in the program and call constructors like MAKE-SEQUENCE to convert the
program into the internal form expected by PARTIAL-EVAL.)

The necessary extension to PARTIAL-EVAL involves an extra clause in the COND
that dispatches on the type of the expression being partially evaluted.  The
provided procedure PARTIAL-EVAL-LIST does most of what we want, except that
it takes a list of expressions as an argument, not a sequence.  So, the
extra cond clause we want is:

(define (partial-eval exp)
  (cond ...
        ((sequence? exp)
         (make-sequence (partial-eval-list (sequence-expressions exp))))
        ...))

Here are some examples.

==> (define x 3)
==> (define y (unknown))
==> (define z 4)

==> (partial-eval '(sequence (+ x 2) (if (= z 4) 1 2)))
(sequence 5 1)

==> (partial-eval '(if (= x y) 
                       (sequence (+ y 4) (- z x))
                       (sequence (+ x x) (- y (+ x z))
                                 (if (= y y) 1 2))))
(if (= 3 y)
    (sequence (+ y 4) 1)
    (sequence 6 (- y 7) (if (= y y) 1 2)))

(Notice that the partial evaluator is not smart enough to notice that
the predicate (= y y) will always evaluate true regardless of the value of
y.  The opportunity to simplify the conditional to just 1 is missed.  It is
an amusing exercise to attempt to extend a partial evaluator to locate every
last opportunity for saved work.  Consider, to take some simple examples, 
the expressions (> (+ x 1) x) and (> (* x x) -1).)

[Problem 2]

==> (partial-eval '(sequence (print x) 4 (print y)))
3
(sequence (*the-non-printing-object*) 4 (print y))

Whenever PARTIAL-EVAL finds that it knows the values of all the parts of an
expression, it wants to use the value of the expression in place of the
expression itself in building the result.  Consequently, it sends the
expression to SYSTEM-EVAL in hopes of getting its value.  

In this case, PARTIAL-APPLY discovers that it knows the values of all of the
arguments in the expression (PRINT X), so it sends (PRINT 3) to SYSTEM-EVAL.
This causes a 3 to be printed out.  PRINT, it turns out, returns a certain
odd-looking object as its value; this object is incorporated in the final
sequence.

The partial evaluator is under the delusion that all procedures are useful
just for the values they return and not for the effects that they have on
the world when they are run.  Although this is true of most of the
procedures we have seen so far this term, it is not true of PRINT and other
procedures with "side effects".  PARTIAL-APPLY ought to be told what
procedures have side effects so that it can refrain from SYSTEM-EVALing
applications of them.

[Problem 3]

The CHECK-NUMBER-OF-ARGS procedure CDRs down the PROCEDURE-DEFS list
looking for the right entry.  If it finds the entry it is looking for then
it returns either true or false according to whether the required number of
arguments recorded in the entry matches the actual number of provided
arguments.  If the list runs out, then the procedure is not in the table.
In this case the number of arguments is assumed to be correct and true is
returned.

(define (check-number-of-args procedure number)
  (define (args-iter proc-defs)
    (cond ((null? proc-defs) t)
          ((eq? (proc-def-op (car proc-defs)) procedure)
           (= (proc-def-args (car proc-defs)) number))
          (else (args-iter (cdr proc-defs)))))
  (args-iter procedure-defs))

The new PARTIAL-APPLY should look something like this:

(define (partial-apply procedure arguments)
  (cond ((not (check-number-of-args procedure (length arguments)))
         (error "Wrong number of arguments" (cons procedure arguments)))
        ((list-known? arguments)
         (system-eval (cons procedure arguments)))
        (else
         (cons procedure arguments))))

Here are some examples.

==> (partial-eval '(sqrt 4.3 1.9))
produces an error

==> (partial-eval '(+ x y z))
does too, because in this language + takes just two arguments

==> (partial-eval '(if (> x y) (sqrt z) (- z x)))
(if (> 3 y) 2 1)


[Problem 4]

This is the hard one.  The problem suggests breaking the problem down 
by putting this clause in the PARTIAL-EVAL conditional:

(define (partial-eval exp)
  (cond ...
        ((cond? exp)
         (check-first-clause-true
           (throw-away-false-clauses
             (partial-eval-clauses (cond-clauses exp)))))
        ...))

This is a good idea.  There are two procedures to write:


PARTIAL-EVAL-CLAUSES takes a list of clauses and returns another list of 
   clauses, in which all of the predicates and consequents of the first 
   list of clauses have been partially evaluated.

(define (partial-eval-clauses clauses)
  (if (null? clauses)
      nil
      (cons (partial-eval-clause (car clauses))
            (partial-eval-clauses (cdr clauses)))))
(define (partial-eval-clause clause)
  (make-clause (partial-eval (clause-predicate clause))
               (partial-eval (clause-consequent clause))))

It's not utterly necessary to define PARTIAL-EVAL-CLAUSE as a separate
procedure, but it makes things clearer.


THROW-AWAY-FALSE-CLAUSES takes a list of partially evaluated clauses and 
   returns those elements of that list whose predicates are not known to 
   be false.

(define (throw-away-false-clauses clauses)
  (cond ((null? clauses) nil)
        ((and (known? (clause-predicate (car clauses)))
              (not (clause-predicate (car clauses))))
         (throw-away-false-clauses (cdr clauses)))
        (else
         (cons (car clauses)
               (throw-away-false-clauses (cdr clauses))))))

This procedure CDRs down the list of clauses, ignoring the clauses whose
predicates are known and false and recursively CONSing up a list of the
other clauses.


Here are some examples.  When concocting test cases for a program, one
should be sure to exercise all of its features.


Here is an example whose first predicate is known to be true.

==> (partial-eval '(cond ((= x (- z 1)) (+ x y))
                         ((> z y) z)
                         (else y)))
(+ 3 y)


Here is an example with some false predicates:

==> (partial-eval '(cond ((> x z) (- x z))
                         ((= x y) x)
                         ((< x z) (- z y))
                         ((= 1 0) (+ x y))
                         (else 0)))
(cond ((= 3 y) 3)
      (#!true (- 4 y))
      (#!true 0))

This outcome is correct, if not very smart.


Here is an example in which the first non-false predicate is true:

==> (partial-eval '(cond ((> x z) (- x z))
                         ((< x z) (- z y))
                         ((= x y) x)
                         (else 0)))
(- 4 y)

Since THROW-AWAY-FALSE-CLAUSES has thrown out the first clause, the second 
clause has come to the front of the list by the time CHECK-FIRST-CLAUSE-TRUE 
is called.  Since the predicate of the second clause is known to be true,
the (partially evaluated) consequent of the second clause is returned.


Here is an example in which all of the predicates are known to be false:

==> (partial-eval '(cond ((> x z) (+ y 4))
                         ((= 1 0) 1)
                         ((= z 9) -7)))
()

