Structure and Interpretation of Computer Programs. Notes

I’ve been studying this fantastic textbook  from  Harold Abelson and Gerald Jay Sussman, the creators of the Scheme language. These are some notes I’ve been taking along the way:

1. The Elements of programming

When designing/evaluating a language there are three key mechanisms to look for:

  • Primitive expressions: The simplest entities in the language
  • Means of combination: By which compound elements are built from simpler ones
  • Means of abstraction: By which compound elements can be named and manipulated as units

We deal with two kinds of elements: Data and procedures

  • Data: The “stuff” that we want to manipulate
  • Procedures: Description of rules for manipulating data

In the Scheme language:
Primitives: Numbers, arithmetic operators, …
Means of combination: Nesting of combinations*
Means of abstraction: define, Compound procedures

*combination -> Operator and arguments. eg (+ 1 2 3) is a combination (+ (+ 1 2) 3) is a nested combination

1.1 Case analysis

(cond ( )
      ( )
      ...
      ( )
)

;;Usage example:
(define (abs x)
    (cond ( (= x 0) 0 )
          ( (< x 0) (- x) )
          ( else x )
    )
)

1.2 Example: Square root by Newton’s method

Newton method: Whenever we have a guess “y” for the sqrt of a number “x”, if the guess is good enough we are done, otherwise, we average y with x/y to get a better approximation

(define (sqrt-newton x)
    (sqrt-iter x 1.0)
)
    
(define (sqrt-iter x y )
    ( cond ( (guess-is-good-enough y x) y )
           ( else (sqrt-iter x (improve-guess y x) ) )
    )
)

(define (guess-is-good-enough y x )
    ( < (abs (- x (* y y) ) ) 0.001 )
)

(define (improve-guess y x) 
    ( average y (/ x y) )
)

(define ( average x y )
    (/ (+ x y) 2 )
)

1.2.1 Internal definitions

The problem with the previous program is that the only procedure important to users is sqrt-newton, the other procedures only clutter up their minds and the global namespace
To solve that problem, procedures are allowed to have internal definitions that are local to that procedure.

(define ( sqrt-newton x )
    (define (sqrt-iter x y )
        ( cond ( (guess-is-good-enough y x) y )
               ( else (sqrt-iter x (improve-guess y x) ) )
        )
    )
    (define (guess-is-good-enough y x )
        ( < (abs (- x (* y y) ) ) 0.001 )
    )
    (define (improve-guess y x) 
        ( average y (/ x y) )
    )
    (define ( average x y )
        (/ (+ x y) 2 )
    )
    (sqrt-iter x 1.0)
)

Now we can simplify the internal procedures because it is not necessary to pass x to each procedure that needs it since they are already in the scope of the first x

(define ( sqrt-newton x )
    (define (sqrt-iter y)
        ( cond ( (guess-is-good-enough y) y )
               ( else (sqrt-iter (improve-guess y) ) )
        )
    )
    (define (guess-is-good-enough y )
        ( < (abs (- x (* y y) ) ) 0.001 )
    )
    (define (improve-guess y) 
        ( average y (/ x y) )
    )
    (define (average v0 v1 )
        (/ (+ v0 v1) 2 )
    )
    (sqrt-iter 1.0)
)

1.3 Procedures and the processes they generate

Procedure: Description of rules for manipulating data
Process: The actual computations generated by a the procedure

A procedure generates a process when it is executed.

(define (factorial n) 
    (define (fact i result) 
        (cond 
            ( (= i 0) result )
            ( (fact (- i 1)(* result i) )) 
        )
    )
    (fact n 1)
)

;;Applying the substitution model
;;(factorial 3)
;;(fact 3 1)
;;(fact 2 3)
;;(fact 1 6)
;;(fact 0 6)
;;6

(define (factorial n)
    ( cond
        ( (= n 0) 1 )
        ( (* n (factorial (- n 1))))
     )
)

;;Applying the substitution model
;;(factorial 3)
;;(* 3 (factorial 2))
;;(* 3 (* 2 (factorial 1)))
;;(* 3 (* 2 (* 1 (factorial 0))))
;;(* 3 (* 2 1 ) )
;;( * 3 2 )
;;6

Both procedures are recursive (ie the procedure definition refers to the procedure itself) but they generate two very different processes:

  • In the first case, it generates a “linear iterative process” ( All the state can be summarized by a fixed number of variables (in this case <> and <>)
  • In the second case it generates a “linear recursive process”. The substitution model reveals a shape of expansion followed by contraction. The expansion occurs as the process build up a chain of deferred operations, in this case, a chain of multiplications. The contraction occurs as the operations are actually performed. In this case, there is hidden state maintained by the interpreter (in the stack)

1.4 Higher-Order procedures

A higher-order procedure is a procedure that can accept other procedures as arguments and/or return procedures as values. Higher-order procedures can serve as a powerful abstraction mechanism, increasing the expressive power of the language.

Consider the following two procedures:

(define (sum-integers a b)
    (if (> a b) 
        0
        (+ a (sum-integers (+ a 1) b) )
    )
)

(define (sum-cubes a b)
    (if (> a b) 
        0
        (+ (* a a a) (sum-cubes (+ a 1) b) )
    )
)
;;These two procedures clearly share a common pattern:
;;They express a summation:

(define (summation term a next b)
    (if (> a b)
        0
        (+ (term a) (summation term (next a) next b))
    )
)

;;Now, sum-integers can be defined as:

(define (inc n) (+ n 1))
(define (identity n) n)
(define (sum-integers a b) (summation identity a inc b ))

;;And sum-cubes:

(define (cube n) (* n n n))
(define (sum-cubes a b) (summation cube a inc b))

Once we have summation, we can use it as a building block in formulating further concepts. For instance, the definite integral of a function f can be approximated numerically using the formula

Integral( f[a b] ) = summation( f(a + dx/2) , f(a + dx + dx/2), f(a + 2dx + dx/2) …) * dx

(define ( integral f a b dx )
    (define (add-dx x) (+ x dx))
    ( * (summation f (+ a (/ dx 2.0)) add-dx b) dx )
)

1.5 Constructing procedures using lambda

In previous example, it seems awkward to have to define trivial procedures such as <> and <> just so we can use them as arguments to higher-order procedures. We can use the special form “lambda”, which creates an unnamed procedure:

(define ( sum-integers a b )
    (summation 
        (lambda(x) x)         ;;Identity
        a 
        (lambda(x)(+ x 1))  ;;Inc
        b 
    )
)

Like any expression that has a procedure as its value, a lambda expression can be used as an operator:

( (lambda(x) (* x x)) 2 )   ;;Applies the procedure defined by the lambda to the parameter 2.

1.5.1 Using <> to create local variables

Another use of lambda is in creating local variables.
For example, suppose we want to compute the function:

f(x,y) = x(1+xy)^2 + y(1-y)+(1+xy)(1-y)

We could use local variables to store 1+xy and 1-y so we don’t need to compute its value multiple times. Say a=1+xy and b=1-y, then the expression is
f(x,y) = x*a + y*b + a*b

One way to accomplish this is to use an auxiliary procedure to bind the local variables:

(define (f x y)
    (define (aux a b)
        (+ (* x (square a)) (* y b) (* a b) )
    )
    (aux (+ 1.0 (* x y)) (- 1.0 y)))
)

We could use a lambda expression to specify an anonymous procedure for binding our local variables

(define (f x y)  ( (lambda (a b) (+ (* x (square a)) (* y b) (* a b) ) ) (+ 1.0 (* x y)) (- 1.0 y) ))

This construct is so useful that there is a special form called let to make its use more convenient:

( let (
        ( )
        ( )
        ...
      )
     
)

;;The previous example using let instead of lambda procedure:
(define (f x y)
    (let ( 
            (a (+ 1 (* x y) ))
            (b (- 1.0 y))
         )
         (+ (* x (square a)) (* y b) (* a b))
    )
)

1.5.2 Procedures as returned values

We can achieve more expressive power by creating procedures whose returned values are themselves procedures

(define (average x y) (/ (+ x y) 2))
(define (average-damp f)  (lambda (x) (average x (f x))))

average-damp returns a procedure given a procedure f. The resulting procedure takes one argument (x) and computes the average between x and f(x)

Now we can use

( (average-damp square) 10 ) -> Will return (10 + 10^2) / 2  = 55

2. Building abstractions with Data

How to build more complex data objects (compound data objects)

2.1 Introduction to Data Abstraction

Data abstraction is a methodology that enables us to isolate how a compound data object is used from how it is constructed from more primitive data objects

The basic idea is to structure programs that are to use compound data objects so that they operate on “abstract data”. At the same time, a “concrete” data representation is defined independent of the programs that use the data.

The interface between those two parts of the system will be a set of procedures, called CONSTRUCTORS and SELECTORS.

2.1.1 Example: Arithmetic operations for Rational Numbers

We want to be able to add, subtract, multiply and divide rational numbers.

First, lets assume (using wishful thinking) we have a way of constructing a rational number form a numerator and denominator and that we have a way of selecting its numerator and denominator. So we have an interface like this:

  • (make-rat numer denom)  ;;Constructor. Returns a rational number
  • (numer x) ;; Selector. Returns the numerator of the rational number x
  • (denom x) ;; Selector. Returns the denominator of the rational number x 

Now we can define a procedure to add two rational numbers n1/d1 + n2/d2 = (n1*d2 + n2*d2) / (d1*d2) as:

 (define (add-rat x y)
   (make-rat ( + (* (numer x) (denom y)) (* (numer y) (denom x))) (* (denom x) (denom y) ))
 )

Now, we have operations on rational numbers defined in terms of its constructor and selectors and provided someone implements that data structure and follows correctly the contract specified by the API defined it should compute the correct result.

2.1.1.2 Pairs

Scheme provides a compound data structure called a “pair”, which can be constructed with the primitive procedure “cons”. cons takes two arguments and returns a compound data object that contains the two arguments as parts. Given a pair, we can extract the parts using the primiitive procedures “car” and “cdr”.

(define x (cons 1 2))
(car x) -> 1
(cdr x) -> 2

cons can be used to form pairs whose elements are pairs, and so on:

(define x (cons 1 2))
(define y (cons 3 4))
(define z (cons x y))
(car z) -> (1 . 2)
(car(car z)) -> 1
(cdr(car z)) -> 2
(car(cdr z)) -> 3
(cdr(cdr z)) -> 4

This ability to combine pairs means that pair can be used as a general-purpose building block to create all sorts of complex data structures. Data objects constructed from pairs are called list-structured data

Now we can easily implment the rational numbers interface using pairs

(define (make-rat a b) (cons a b))
(define (numer x) (car x))
(define (denom x) (cdr x))
(define (print-rat x) (newline) (display (numer x)) (display “/”) (display (denom x)))

We can even change make-rat so it reduces the rational number to lowest terms using the greatest common denominator

(define (make-rat a b) ( let (( g (gcd a b) )) (cons (/ a g) (/ b g))))

2.1.3 What is meant by Data?

As seen on the previous example, we could argue that the rational number data structure is any representation that can fulfill the contract determined by the API (make-rat, numer and denom)
The same is true for the pair implementation, that is, the operation that staisfy the condition that for any object x and y, if z is (cons x y), the (car z) is x and (cdr z) is y.

We could implment cons, car and cdr whihout using ant data structures at all but only using procedures:

(define (cons x y)
    (define (dispatch m)
        (cond ( (= m 0) x)
              ( (= m 1) y)
              (else (error "Wrong argument"))
        )
    )
    dispatch
)

(define (car z) (z 0))
(define (cdr z) (z 1))

cons returns a procedure which is then used by car and cdr to get the first and second element of the pair

2.2 Hierarchical data and the closure property

The fact that cons can create pairs whose elements are pairs is refered as the closure property.

Closure: An operation for combining data objects satisfies the closure property if the results of combining things with that operation can themselves be combined using that same operation

Closure permits us to create hierarchical structures — structures mas up of parts, which themselves are made of parts and so on

2.2.1 Representing sequences

Sequence: An ordered collection of data objects.

(cons 1
      (cons 2
            (cons 3
                  (cons 4 '())
            )
      )
)

Such a sequence of pairs, is called a list and scheme provides a primitive called list to help constructing them
(list 1 2 3 4 )

Some functions on lists:

(define (length l)
    (if (null? l) 0
                  (+ 1 (length (cdr l)))
    )
)

(define (list-ref l n)
    (define (list-ref-iter l i)
        (if (= i n) (car l)
                    (list-ref-iter (cdr l) (+ i 1))
        )
    )
    (if (>= n (length l) ) (display "Error")
                           (list-ref-iter l 0)
    )
)

(define (reverse l)
  (if (null? l)
                 '()
                (append (reverse1 (cdr l)) (list (car l)))
  )
)

(define (reverse l)
    (define (reverse-iter l r)
        (if (null? l ) r
                       (reverse-iter (cdr l) (cons (car l) r) )
        )
    )
    (reverse-iter l '())
)

2.2.1.1 Mapping over list

A useful operation is to apply some transformation to each element in a list and generate a list of results

(define (map-list f l )
    (if (null? l) '()
                  (cons (f (car l)) (map-list f (cdr l)))
    )                     
)

Now we can define scale-list to scale all the items on the list by a constant factor
(define (scale-list l factor) (map-list (lambda(x)(* x factor)) l))

2.2.2 Hierarchical Structures

A sequence whose elements are sequences is a tree. The elements of the sequence are the branches of the tree, and elements that are themselves sequences are subtrees

(cons (cons (cons 1 2) 3) 4)

                ( (1 2) 3 4)
                   /     \  \
                 /        \   \
        (1 2)   /          \   \
               /\           3   4
              /  \
             1   2

Recursion is a natural tool for dealing with tree structures since we can often reduce operations on trees to operations on their branches, which reduces to operations on their branches and so on

(define ( count-leaves x )
            (cond   ((null? x) 0)
                    ((not (pair? x)) 1)
                    (else (+ (count-leaves (car x) )
                             (count-leaves (cdr x))))))

2.2.3 Sequences as conventional interfaces

Most data transformations can be achieved using these key elements

Enumerator: Selects elements from a data set (e.g. Leaf nodes in a tree, elements of a list, etc…)
Map: Applies a procedure to a data element
Filter: Selects elements of a data set based on some predicate
Accumulator: Accumulates results (e.g. append transformed element to a list, add transformed element to the sum of previous transformed elements, etc. )

For example, two different programs:

a) A program that computes the sum of the squares of the leaves that are odd in a tree,
b) A program that constructs a list of all the even fibonacci numbers Fib(k), where k is less than or equal to a given integer

We can express those two programs like this:

a)    enumerate: tree leaves –> filter: odd? –> map: square –> accumulate: +, 0
b)    enumerate: integers —-> map: Fib —-> filter: even? –> accumulate: cons, ()

We have already implemented map. We can implement filter like follows:

(define ( filter predicate sequence )
    (cond
           ((null? sequence) '())
           ((predicate (car sequence)) (cons (car sequence) (filter predicate (cdr sequence))))
           (else (filter predicate (cdr sequence)))
    )
)

;;Ex: (filter even? (list 1 2 3 4)) --> (2,4)

;;And a generic accumulator:

(define (accumulate op initial sequence)
    ( if (null? sequence ) initial
                           ( op (car sequence) (accumulate op initial (cdr sequence)))
    )
)

Ex: (accumulate + 0 (list 1 2 3 4 5))    --> 15
    (accumulate cons '() (list 1 2 3 4)) --> (1 2 3 4 )

And finally the enumerators. The enumerator will generate a sequence of elements to be processed.

We can define a procedure to enumerate integers in a given interval

(define (enumerate-interval low high)
    (if (> low high ) '()
                      (cons low (enumerate-interval (+ low 1) high))
    )
)

;;Ex: (enumerate-interval 2 5) --> (2 3 4 5)

;;And an enumerator for leves on a tree

(define (enumerate-tree tree)
    (cond 
          ((null? tree) '())
          ((not (pair? tree)) (list tree))
          (else (append (enumerate-tree (car tree))
                        (enumerate-tree (cdr tree))
                )
          )
    )
)

;;Ex: (enumerate-tree (list 1( list 2( list 3 4)) 5)) --> ( 1 2 3 4 5)

;;Now, we can implement those programs (a and b) using these building blocks:

;;a)
(define (sum-odd-squares tree)
    (accumulate + 0 ( map (lambda(x)(* x x)) (filter odd? ( enumerate-tree tree ))))
)

;;Ex: (sum-odd-squares(list 1( list 2( list 3 4)) 5)) --> 1^2 + 3^2 + 5^2 = 35

;;b)
(define (even-fibs n)
    ( accumulate cons '() ( filter even? ( map fib (enumerate-interval 0 n))))
)

;;We need a function to generate fibonnaci numbers
(define (fib n)
    (define (fib-iter i f0 f1)
        (if (= i n) f0
                    (fib-iter (+ i 1) f1 (+ f1 f0))
        )
    )
    (fib-iter 0 0 1)
) 

;;Ex: (even-fibs 10) --> (0 2 8 34)

2.2.4 Example: A Picture Language

To describe the language, we have to focus on its primitives, its means of combination and its means of abstraction

In this picture language, there is only one kind of element, called a painter. A painter draws an image that is shifted and scaled to fit within a designated parallelogram-shaped frame.

To combine images, we use various operations that construct new painters from given painters. For example, the beside operation takes two painters and produces a new, compound painter that draws the fist painter’s image on the left half of the frame, and the second painter’s image on the right half.

In building up a complex image in this manner we are exploiting the fact that painters are closed under the language’s means of combination. The beside or below of two painters is itself a painter; therefore, we can use it as an element in making more complex painters.

Racket implementation:

(require graphics/graphics)
(open-graphics)


;;viewport 
(define (make-view title width height)
    ( let(( viewport (open-viewport title width height)))
         (lambda(s) ( cond 
                      ( (= s 0) title)
                      ( (= s 1) width)
                      ( (= s 2) height)
                      (else viewport)
                    )
        )
    )
)
(define (view-title view) (view 0))
(define (view-width view) (view 1))
(define (view-height view) (view 2))
(define (view-viewport view) (view 3))
(define (clear-view view) ( (clear-viewport (view-viewport view) ) ) )


;vec2
(define (make-vec2 x y) (cons x y))
(define (vec2-x v) (car v))
(define (vec2-y v) (cdr v))
(define (scale-vec2 s v) (make-vec2 (* s (vec2-x v)) (* s (vec2-y v))))
(define (add-vec2 v0 v1) (make-vec2 (+ (vec2-x v0) (vec2-x v1)) (+ (vec2-y v0) (vec2-y v1))))
(define (sub-vec2 v0 v1) (make-vec2 (- (vec2-x v0) (vec2-x v1)) (- (vec2-y v0) (vec2-y v1))))

;Segment
(define (make-segment v0 v1) (cons v0 v1))
(define (start-segment s) (car s))
(define (end-segment s) (cdr s))

;Frame
(define (make-frame origin edge0 edge1 )
    (lambda(s) (cond( (= s 0) origin )
                    ( (= s 1) edge0 )
                    ( (= s 2) edge1 )
                )
    )
)
(define (origin-frame f) (f 0))
(define (edge0-frame f) (f 1)) 
(define (edge1-frame f) (f 2))

;;Generates a procedure which can be used to transform points to the given frame
(define (frame-coord-map frame)
    (lambda (v)
        (add-vec2    (origin-frame frame)
                    (add-vec2 (scale-vec2 (vec2-x v)(edge0-frame frame))
                              (scale-vec2 (vec2-y v)(edge1-frame frame)))
        )
    )
)

;Draws a segment to a viewport
(define (draw-segment seg viewport) 
    (let (
            (start (start-segment seg) )
            (end (end-segment seg ) )
           )
           (( draw-line viewport ) (make-posn (vec2-x start) (vec2-y start) ) (make-posn (vec2-x end) (vec2-y end) ))
    )
)
                            

;;Returns a procedure that given a frame, draws the segment-list
(define (make-painter segment-list)
    (lambda (frame viewport)
        (for-each
            (lambda (segment)
                (draw-segment (make-segment ((frame-coord-map frame) (start-segment segment)) ((frame-coord-map frame) (end-segment segment))) viewport)
            )
            segment-list
        )
    )
)

;;Creates a new painter (procedure with a frame as an argument) which applies a transformation to the frame
(define (transform-painter painter origin corner1 corner2)
    (lambda (frame viewport)
        (let ((m (frame-coord-map frame)))
            (let ((new-origin (m origin)))
                (painter (make-frame new-origin (sub-vec2 (m corner1) new-origin) (sub-vec2 (m corner2) new-origin) ) viewport )
            )
        )
    )
)

;Rotates the painter 90 degres clockwise
(define (rotate90 painter)
    (transform-painter painter
                       (make-vec2 1.0 0.0)
                       (make-vec2 1.0 1.0)
                       (make-vec2 0.0 0.0)
    )
)

;Flip vertically
(define (flip-vert painter)
    (transform-painter painter
                       (make-vec2 1.0 1.0)
                       (make-vec2 0.0 1.0)
                       (make-vec2 1.0 0.0)
    )
)

;Flip horizontally
(define (flip-horiz painter)
    (transform-painter painter
                       (make-vec2 1.0 0.0)
                       (make-vec2 0.0 0.0)
                       (make-vec2 1.0 1.0)
    )
)

;Creates a new painter from two given painters that draws the fist painter's image 
;on the left half of the frame, and the second painter's image on the right half.
(define (beside p0 p1)
    (define (left painter) (transform-painter painter
                                              (make-vec2 0.0 0.0)
                                              (make-vec2 0.5 0.0)
                                              (make-vec2 0.0 1.0))
    )
    (define (right painter) (transform-painter painter
                                               (make-vec2 0.5 0.0)
                                               (make-vec2 1.0 0.0)
                                               (make-vec2 0.5 1.0))
    )

    (lambda (frame viewport) 
        ( (left p0) frame viewport)
        ( (right p1) frame viewport)
    )
)            


;Creates a new painter from two given painters that draws the fist painter's image 
;on the top half of the frame, and the second painter's image on the bottom half.
(define (below p0 p1)
    (define (top painter) (transform-painter painter
                                             (make-vec2 0.0 0.0)
                                             (make-vec2 1.0 0.0)
                                             (make-vec2 0.0 0.5))
    )
    (define (bottom painter) (transform-painter painter
                                                (make-vec2 0.0 0.5)
                                                (make-vec2 1.0 0.5)
                                                  (make-vec2 0.0 1.0))
    )
    (lambda (frame viewport) 
        ( (top p0) frame viewport)
        ( (bottom p1) frame viewport)
    )
)

;Creates a new painter from two given painters that draws one on top
;of the other
(define (overlay p0 p1)
    (lambda (frame viewport) 
        (p0 frame viewport)
        (p1 frame viewport)
    )
)

;;We can use beside and below to build more complex painters
(define (horizontal-split painter n)
    (if (= n 0)    painter
                (beside painter (horizontal-split painter (- n 1)))
    )
)

(define (vertical-split painter n)
    (if (= n 0)    painter
                (below painter (vertical-split painter (- n 1)))
    )
)

(define (square-of-four p0 p1 p2 p3)
    (let (
            (top (beside p0 p1))
              (bottom (beside p2 p3))
         )
         (below bottom top)
    )    
)

(define (split p0 p1)
    (define (proc painter n)
        ( if (= n 0) painter
                    (let((smaller (proc painter (- n 1))))
                        (p0 painter (p1 smaller smaller))
                    )
        )
    )
    proc
)

(define right-split (split beside below))
(define up-split (split below beside))

(define (corner-split painter n)
    (if (= n 0) painter
                (let(
                        (up (up-split painter (- n 1)))
                        (right (right-split painter (- n 1)))
                    )
                    (let( 
                            (top-left (beside up up))
                               (bottom-right (below right right))
                              (corner (corner-split painter (- n 1)))
                        )
                           ( beside (below painter top-left) (below bottom-right corner) )
                    )
                )
    )
)

(define (square-limit painter n)
        (let ((quarter (corner-split painter n)))
            (let ((half (beside (flip-horiz quarter) quarter)))
                 (below (flip-vert half) half)
            )
        )
)

(define (draw painter view)
    (painter (make-frame (make-vec2 0 0) (make-vec2 (view-width view) 0.0) (make-vec2 0.0 (view-height view))) (view-viewport view))
)

;;Example usage
(define arrow-segments (list (make-segment (make-vec2 0.5 0.0) (make-vec2 0.0 1.0))
                       (make-segment (make-vec2 0.0 1.0) (make-vec2 0.5 0.75))
                       (make-segment (make-vec2 0.5 0.75) (make-vec2 1.0 1.0))
                       (make-segment (make-vec2 1.0 1.0) (make-vec2 0.5 0.0))))


(define half-figure-segments (list (make-segment (make-vec2 0.5 0.0) (make-vec2 0.25 0.25))
                       (make-segment (make-vec2 0.25 0.25) (make-vec2 0.375 0.375))
                       (make-segment (make-vec2 0.375 0.375) (make-vec2 0.0 0.5))
                       (make-segment (make-vec2 0.0 0.625) (make-vec2 0.25 0.5625))
                       (make-segment (make-vec2 0.25 0.5625) (make-vec2 0.25 1.0))
                       (make-segment (make-vec2 0.375 1.0) (make-vec2 0.375 0.75))
                       (make-segment (make-vec2 0.375 1.0) (make-vec2 0.375 0.75))
                       (make-segment (make-vec2 0.375 0.75) (make-vec2 0.5 0.75))))
                       

(define arrow-painter (make-painter arrow-segments) )
(define figure-painter (let((half (make-painter half-figure-segments)))(overlay half (flip-horiz half))))
(define view (make-view "Viewport" 500 500) )
( draw (square-limit (below figure-painter arrow-painter) 4) view )
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s