1.12 Exercise 1.12

This procedure calculates the value in Pascal’s triangle at depth depth and column column. Both the depth and column are 0-indexed.

(define (pascal-value depth col)
  (cond ((= 0 col) 1)
        ((= depth col) 1)
        (else (+ (pascal-value (- depth 1) (- col 1))
                 (pascal-value (- depth 1) col)))))

We can define two more procedures to (primitively) print the triangle:

(define (make-new-row depth)
  (define (loop depth col str)
    (if (> col depth) str
        (string-append str
                       (number->string (pascal-value depth col))
                       (loop depth (+ col 1) " "))))
  (loop depth 0 ""))
(define (pascals-triangle depth)
  (display (make-pascal-triangle 0 depth "")))

However, I’m not satisfied with this recursive solution. It is clear to me that, instead of tree-recursively generating every value in the tree individually, we can generate the next row of the tree given the bottom row in the current one. We can do this by storing the tree as a list of lists (where new rows are added to the front of the list), and making new rows of the tree by making a list of all the pairs of values in the last row and mapping + over them.

Of course, I could’ve also calculated values using binomial coefficients by row, but I’m more interested in treating this like a data structure juggling problem. Constructing and extending lists of lists can be tricky (especially since we don’t have mutation yet), and it happens that this is a good problem for exploring one way to do this.

It is crucial to the simplicity of implementation that the lists of lists be constructed by prepending – making a new list by consing the new inner list with the old list itself. I haven’t bothered to reverse the rows of the entire tree, so it appears upside-down – but luckily for us, every row is the same forwards and backwards, so this construction has no effect on the tree itself.

I much prefer having a data structure holding an instance of Pascal’s triangle collected and returned. It seems to me that writing procedures consuming this structure can be more general than those that need to directly generate values in the triangle themselves, as seen above.

There is a trick in make-pairs: I add a new pair (0 1) to the front and back of the pairs list. This is done to automatically generate the 1s on the outside of the triangle without special cases. Also, make-pairs will generate no pairs other than these padding ones for the row at depth 0, (1), because (cdr (1)) is null.

Unlike the exercise directs, this procedure also generates an iterative process.

(define (empty? l)
  (eq? nil l))
(define (pascal-triangle depth)
  (define (make-pairs l)
    (define (loop l pairs)
      (if (null? (cdr l)) (cons '(0 1) pairs)
          (loop (cdr l)
                (cons (list (car l) (cadr l)) pairs))))
    (loop l '((0 1))))
  (define (compute-next-row row)
    (if (empty? row) '(1)
        (map
         (lambda (p) (+ (car p) (cadr p)))
         (make-pairs row))))
  (define (triangle-loop current-depth current-triangle)
    (if (> current-depth depth) current-triangle
        (triangle-loop (+ current-depth 1)
                       (cons (compute-next-row (car current-triangle))
                             current-triangle))))
  (triangle-loop 1 '((1))))