2011-10-11

SICP Exercise 2.46: Vectors

A two-dimensional vector v running from the origin to a point can be represented as a pair consisting of an x-coordinate and a y-coordinate. Implement a data abstraction for vectors by giving a constructor make-vect and corresponding selectors xcor-vect and ycor-vect. In terms of your selectors and constructor, implement procedures add-vect, sub-vect, and scale-vect that perform the operations vector addition, vector subtraction, and multiplying a vector by a scalar:
(x1, y1) + (x2, y2) = (x1 + x2, y1 + y2)
(x1, y1) - (x2, y2) = (x1 - x2, y1 - y2)
        s · (x, y) = (sx, sy)
As a two-dimensional vector has only two pieces of data associated with it, the x- and y-coordinates, it lends itself naturally to being represented as a pair. Let's put x as the first elements, and y as the second. We can then access them via car and cdr respectively:
(define (make-vect x y) (cons x y))
(define (xcor-vect v) (car v))
(define (ycor-vect v) (cdr v))
Given this representation (or at least this constructor and these selectors), we can implement the given operations straightforwardly. We simply need to extract the x- and y-coordinates from the vectors we're dealing with using the selectors, perform the appropriate mathematical operations on them and then create a new vector as our result using the constructor:
(define (add-vect v1 v2)
  (make-vect (+ (xcor-vect v1) (xcor-vect v2))
             (+ (ycor-vect v1) (ycor-vect v2))))

(define (sub-vect v1 v2)
  (make-vect (- (xcor-vect v1) (xcor-vect v2))
             (- (ycor-vect v1) (ycor-vect v2))))

(define (scale-vect s v)
  (make-vect (* s (xcor-vect v))
             (* s (ycor-vect v))))
Let's see them in action:
> (define v1 (make-vect 4 5))
> (define v2 (make-vect 7 3))

> v1
'(4 . 5)
> v2
'(7 . 3)

> (xcor-vect v1)
4
> (ycor-vect v1)
5
> (ycor-vect v2)
3
> (xcor-vect v2)
7

> (add-vect v1 v2)
'(11 . 8)
> (add-vect v2 v1)
'(11 . 8)

> (sub-vect v1 v2)
'(-3 . 2)
> (sub-vect v2 v1)
'(3 . -2)

> (scale-vect 3 v1)
'(12 . 15)
> (scale-vect 2 v2)
'(14 . 6)

SICP Exercise 2.45: Splitting the splitters

Right-split and up-split can be expressed as instances of a general splitting operation. Define a procedure split with the property that evaluating
(define right-split (split beside below))
(define up-split (split below beside))
produces procedures right-split and up-split with the same behaviors as the ones already defined.

What we want split to do here is to return a procedure that takes a painter and some integer value, n that applies the supplied splitting operations in the given order. As we noted in exercise 2.44, right-split and up-split are very similar. Here they are again, but with the differences highlighted:
(define (right-split painter n)
  (if (= n 0)
      painter
      (let ((smaller (right-split painter (- n 1))))
        (beside painter (below smaller smaller)))))

(define (up-split painter n)
  (if (= n 0)
      painter
      (let ((smaller (up-split painter (- n 1))))
        (below painter (beside smaller smaller)))))
This gives us the pattern for the procedure we want split to return. We just need to replace the calls to below and beside with the appropriate calls to the supplied splitting operations. We can define this as an inner procedure for split and then return a lambda expression that invokes the procedure with the appropriate values:
(define (split large-splitter small-splitter)
  (define (splitter painter n)
    (if (= n 0)
        painter
        (let ((smaller (splitter painter (- n 1))))
          (large-splitter painter (small-splitter smaller smaller)))))
  (lambda (painter n) (splitter painter n)))
We can then use this to produce new versions of right-split and up-split:
(define right-split (split beside below))
(define up-split (split below beside))
...and then try them out. Invoking:
(right-split spiral 4)
...produces:
While...
(up-split spiral 4)
...produces:

SICP Exercise 2.44: Splitting Up

Define the procedure up-split used by corner-split. It is similar to right-split, except that it switches the roles of below and beside.

up-split is very similar... Here's right-split:
(define (right-split painter n)
  (if (= n 0)
      painter
      (let ((smaller (right-split painter (- n 1))))
        (beside painter (below smaller smaller)))))
To produce up-split we simply rename the procedure and recursive call and switch below and beside around to give:
(define (up-split painter n)
  (if (= n 0)
      painter
      (let ((smaller (up-split painter (- n 1))))
        (below painter (beside smaller smaller)))))
Using the spiral we produced earlier we can put this to the test:
((up-split spiral 3) window)
...produces the following: