In lab last week, you saw your first sorting program: a program which
consumes a list of numbers and produces a list of the same numbers,
only in sorted order.  Today, we want to look at another sorting
algorithm, called quicksort (so called because it is ... quicker than
other sorting algorithms on large examples).

The algorithm works as follows: 
  - call the first element of the list the pivot
  - divide the rest of the list into two pieces: those that are
    smaller than the pivot, and those that are larger than the pivot.
  - sort these two new lists
  - form a new list from the sorted list of smaller elementss, the
    pivot, and the sorted list of larger elements.

Let's work through two examples:

1. (list 11 8 14 7)
2. (list 1 5 3 6)

See section 25 of the text for a detailed walk-through of how
quicksort operates on the first example.  The second example is

Now, we want to write the program.  As always, we start with the
contract, purpose, header, and template:

;; qsort : (listof number) -> (listof number)
;; sort the numbers in the list in increasing order
(define (qsort alon)
  (cond [(empty? alon) ...]
        [else ... (first alon) ... (qsort (rest alon)) ...]))

We've written out the steps in English.  Can you fill in the rest of
the program?  Parts of this are easy, based on the description:

(define (qsort alon)
  (cond [(empty? alon) empty]
          (local ((define pivot (first alon)))
	    ... (qsort (rest alon)) ...)]))

What's strange here is that the template isn't doing what we want it
to do.  We don't want to run qsort on the rest of the list, but on the
list of numbers smaller than the pivot and on the list of numbers
larger than the pivot.  In other words, our program needs to do
something like the following:

(define (qsort alon)
  (cond [(empty? alon) empty]
          (local ((define pivot (first alon)))
	    ... (qsort (smaller-items (rest alon) pivot)) 
	    ... (qsort (larger-items (rest alon) pivot)) ...)]))

(Assume for the moment that we've written the helper functions
smaller-items and larger-items.)

Let's first finish the program and look at another example, then come
back and discuss what went wrong with the template:

(define (qsort alon)
  (cond [(empty? alon) empty]
          (local ((define pivot (first alon)))
	    (append (qsort (smaller-items (rest alon) pivot)) 
                    (list pivot)
		    (qsort (larger-items (rest alon) pivot))))]))

(define (smaller-items alon threshold)
  (filter (lambda (n) (< n threshold)) alon))

(define (larger-items alon threshold)
  (filter (lambda (n) (> n threshold)) alon))

Let's consider a second example.  Have you heard of fractals?
Fractals are objects with fractal dimension.  They are interesting to
us because they show similar structure on several different scales.
Let's consider a simple example of a fractal, known as the Sierpinski
triange (see the section on fractals, page 367 in the text, for the
picture of the Sierpinski triangle).

We want to write a program that consumes the three vertices of the
original (equilateral) triangle and draws the Sierpinski triangle,
returning true.  The program only draws triangles whose sides are
longer than a certain small threshold, say 3.  Assume you have a
function draw-triangle, which consumes three points, draws a triangle
between them, and returns true.

The data definition for points (posns, in the text and libraries) is 

;; A posn is a structure
;;  (make-posn x y)
;; where x and y are numbers

(define-struct posn (x y))

How do we get started?  Well, our description of the problem suggests
two cases: one if a side of the triangle is smaller than three and one
if it is not.  Let's defer the size check to a helper function for now:

(define (sierpinski p1 p2 p3)
  (cond [(too-small? p1 p2 p3) true]
        [else ... (draw-triangle p1 p2 p3) ...]))

How do we fill in the else case?  The problem said that we need to
find the midpoints of the three sides, then fill in the three outer
triangles.  The following program does this:

(define (sierpinski p1 p2 p3) 
    [(too-small? p1 p2 p3) true] 
      (local ((define p1-p2 (mid-point p1 p2)) 
              (define p2-p3 (mid-point p2 p3)) 
              (define p3-p1 (mid-point p1 p3))) 
          (draw-triangle p1 p2 p3)     
          (sierpinski p1 p1-p2 p3-p1) 
          (sierpinski p2 p1-p2 p2-p3) 
          (sierpinski p3 p3-p1 p2-p3)))])) 

Let's step back and look at these two programs, qsort and sierpinski.
They are both recursive, yet neither follows the templates that we
have discussed.  What's the difference here?

Where did the recursion come from in the programs that we've been
writing all semester?  From the data.  We used recursively defined
data, so we needed recursive programs to process that data.  Where
does the recursion come from in today's programs?  From the problems
that we are writing programs to solve (even though the data is also
recursively defined, as in the case of qsort).  

Both of today's programs use a common problem solving technique called
"divide-and-conquer": we take a problem, break it into smaller
instances of the same problem, solve the smaller instances, and
combine the results.  We use recursion because we are solving
instances of the same problem, but we cannot break the problem into
pieces based solely on the structure of the data.  Instead, we break
the problem into pieces based on the insights that we have about the
problem itself.

Thus, today we introduce a new form of recursion called "generative
recursion".  In generative recursion, we generate new instances of a
problem based on some creative insight and solve them recursively.
Our prior problems use what we call "structural recursion": the
recursion comes solely from the recursive structure of the data.  

We know the design recipes for structural recursion.  What about
generative recursion?  Let's look at qsort and sierpinski and try to
find some commonality in their organization.  Notice that both
programs use a cond, with one case for "the problem is small enough to 
solve" and another for "decompose the problem into smaller problems".
As a first approximation to a template, we can therefore write the

(define (gen-recur-func arg1 .. argn)
  (cond [(easy-enough-to-handle? arg1 ... argn) 
         (handle-easy-case arg1 ... argn)]
        [else (combine
                (gen-recur-func smaller-problem1) ...
                (gen-recur-func smaller-problemk) ... )]))

We'll continue refining this template and the design recipe for
generative recursion in the next class.