[previous] [up] [next]     [index]
Next: Designing Abstractions with First-Class Up: Designing Abstractions from Examples Previous: Extended Exercise: Moving Pictures

Note: Designing Abstractions from Templates

At the very beginning of this part of the book, we discussed how we design sets of functions from the same template. More specifically, when we design a set of functions that all consume the same kind of data, we reuse the same template over and over again. It is therefore not surprising that the function definitions look similar and that we will abstract them later.

Indeed, we could abstract from the templates directly. While this topic is highly advanced and still subject of research in the area of programming languages, we can discuss it with a short example. Consider the template for lists:

(define (fun-for-l l)
  (cond 
    [(empty? l) ...] 
    [else ... (first l) ... (fun-for-l (rest l)) ...])) 
It contains two gaps, one in each clause. When we define a list-processing function, we fill these gaps. In the first clause, we typically place a plain value. For the second one, we combine (first l) and (f (rest l)) where f is the recursive function.

We can abstract over this programming task with the following function:

;; reduce : X (X Y -> Y) (listof Y) -> Y
(define (reduce base combine l) 
  (cond 
    [(empty? l) base] 
    [else (combine (first l) 
            (reduce base combine (rest l)))])) 
It consumes two extra arguments: base, which is the value for the base case, and combine, which is a function that performs the value combination for the second clause.

Using reduce we can define many plain list-processing functions as well as almost all the functions of figure [cross-reference]. Here are two of them:

;; sum : (listof number) -> number
(define (sum l) (reduce 0 + l)) 
;; product : (listof number) -> number
(define (product l) (reduce 1 * l)) 

For sum, the base case always produces 0; adding the first item and the result of the natural recursion combines the values of the second clause. Analogous reasoning explains product.

To define sort, we need to define an auxiliary function first:

;; sort : (listof number) -> (listof number)
(define sort 
  (local ((define (insert an alon) 
            (cond 
              [(empty? alon) (list an)] 
              [else (cond 
                      [(> an (first alon)) (cons an alon)] 
                      [else (cons (first alon) (insert an (rest alon)))])]))) 
    (reduce empty insert))) 
Other list-processing functions can be defined in a similar manner.


[previous] [up] [next]     [index]
Next: Designing Abstractions with First-Class Up: Designing Abstractions from Examples Previous: Extended Exercise: Moving Pictures

PLT