Lecture 17: Mathematicians Lie; How to Design Programs

###
Lecture 17: Mathematicians Lie; How to Design Programs

1. first-class functions in mathematics: derivative

   We want to know the slope of a function graph at some point.

   When given f, we really want a function f' that, when given a point on the x-axis, computes the slope of f at that point.

       ;; d/dx : (number -> number) -> (number -> number)
       (define (d/dx f)
         (local ((define (fprime x)
       	    (/ (- (f (+ x epsilon)) (f (- x epsilon)))
       	       (* 2 epsilon)))
       	  (define epsilon .00001))
           fprime))

       ;; Example:
       (define (f1 x) x)

       (define f1-prime (d/dx f1))

       (f1-prime 0.)
       (f1-prime 1.)
       (f1-prime 2.)
       ;; they are all close to 1. but not equal to 1!

       ;; We could also write:
       (= ((d/dx f1) 0.) (f1-prime 0.))

       ;; -----
       (define (f2 x) (+ (* x x) (* 2 x) 1))

       (define f2-prime (d/dx f2))

       (f2-prime 0) ; = 2
       (f2-prime 1) ; = 4
       (f2-prime 2) ; = 6

2. first-class functions in mathematics: integrals

   We are interested in the area under a function graph between two points.

   When given f, we really want a function Sf that, when given two points on the x-axis, computes the area under the graph of f between the two points.

       ;; Sum* : (number -> number) -> (number number -> number)
       ;; computes the indeterminate integral of f [simple version]
       ;;   the area between left and right is approximated as a trapezoid
       (define (Sum f)
         (local ((define (trapezoid left right)
       	    (* (- right left)
       	       (/ (+ (f left) (f right))
       		  2))))
           trapezoid))

       ;; Example:
       (= ((Sum (lambda (x) x)) 0. 1.) .5)

       ---------------------------------------------------------------------------------

       ;; Sum* : (number -> number) -> (number number -> number)
       ;; computes the indeterminate integral of f
       ;;   the area between left and right is approximated as a series of trapezoids
       (define (Sum* f)
         (local ((define (trapezoid left right)
       	    (* (- right left)
       	       (/ (+ (f left) (f right))
       		  2)))
       	  (define (trapezoid* left right)
       	    (local ((define N 10)
       		    (define width (/ (- right left) N))
       		    (define (one i) (trapezoid (+ left (* i width))
       					       (+ left (* (+ i 1) width)))))
       	      (apply + (build-list N one)))))
           trapezoid*))

       ;; Example:
       (= ((Sum* (lambda (x) x)) 0. 1.) .5)

   A First Summary of 210: Designing and "Editing" Programs

3. when we design a program, we MUST start with three most basic steps from the design recipe:
   1. data analysis and possibly data definitions
   2. contract (what goes in, what comes out); header; purpose statement
   3. program examples: if the data definitions are puzzling, start with making input examples

   Now, and only now, we have a choice to make. The choice is

   Do we understand the problem as a problem about domain knowledge?
   Or, do we understand the problem as a CS problem?

   Here are examples of both:
   domain knowledge first: average of a sequence of numbers, vector addition
   CS knowledge first: converting a list of Celsius temperatures into Fahrenheit temperatures, counting the number of names in a family tree

   When we do "domain knowledge first", we will often encouter CS knowledge in the auxiliary functions.

   When we do "CS knowledge first", we will often encounter domain knowledge in the auxiliary functions.

   Assume we do "CS knowledge first":

   1. template, based on input data definition
   2. body definition, based on program examples and template
   3. tests, based on examples and definition
4. At this point we are done with the first big step: creating the program. But, just as we edit, edit, edit papers to improve them, we edit, edit, edit programs to improve them, too. Our first tool of editing is the use of abstractions.

   Our specific choices are:

   Do we abstract similar functions into a single function?
   Or, do we use existing abstractions from our underlying language?

   If we process lists in Scheme, we should use its existing list abstractions:

        build-list : nat-num (nat-num -> X) -> (listof X)
        map : (X -> Y) (listof X) -> (listof Y)
        filter : (X -> bool) (listof X) -> (listof X)
        foldr : (X Y -> Y) Y (listof X) -> (listof Y)
        foldl : (X Y -> Y) Y (listof X) -> (listof Y)
        for-each ... and more

   If we don't process lists but arbitraily large data, we should consider creating our own abstractions.

Matthias Felleisen | This page was generated on Fri Mar 5 09:04:41 CST 1999.