Lecture 2 : Getting Conditioned


Consider our program from last time.

(define (owe s) (* (/ 12 8) s))

We use the function by calling it with an argument: (owe 5).  We call
s the parameter and 5 the argument.

Let's write another program.  Suppose you share in pizza both Saturday
and Sunday nights.  Write a program wkend-pizza that calcuates what
you spent on pizza all weekend.

A note before writing the program.  We've said that programs are how
we communicate calculations to a computer.  But humans, as well as
computers, need to read programs.  For example, you and a colleague
may write programs for the same project and need to look at each
other's programs.  Furthermore, you yourself may look back at a
program a week later and need to remember what calculation you wrote
that program to do.  In these cases, a summary of what the program
does is usually sufficient.  We therefore add documentation to
programs.

This course requires two forms of documentation on all programs: a
contract and a purpose.  The contract summarizes what kinds of
information the program consumes and produces.  The purpose is a one
sentence description (in english) of what the program does.  For our
wkend-pizza program, we have the following contract and purpose:

; wkend-pizza : num num -> num     (contract) 
; Purpose: calculate hours of exercise needed to work off a given
;   number of slices of pizza   (purpose)

The ; at the front of each line indicate that a line of text is a
comment.  Scheme will ignore any text that follows ; on a line.

(define (wkend-pizza sa su)
  (+ (owe sa) 
     (owe su)))

or 

(define (wkend-pizza sa su)
  (owe (+ sa su)))

Is one of these better than the other?  No.

We could also write

(define (wkend-pizza sa su)
  (+ (* (/ 12 8) sa) 
     (* (/ 12 8) su)))

How does this compare to the others?  It's worse in two ways.  First,
if the price of the pizza changes, we have to alter the code in two
places instead of one.  Second, it's harder to read because it
involves a nested subexpression.  This brings us to our first
principle of computing

	Always use an existing function when one is available

Once we've written a program, we need to test it to convince ourselves 
that it does what we intended it to do.  We test a program by running
it on some sample inputs and seeing whether the program produces the
outputs that we expect.

      Test case          Expected Output       Actual Output
  ----------------------------------------------------------------
   (wkend-pizza 2 3)          15/2                 15/2
   (wkend-pizza 6 4)           15                   15

Where do the contents of each column come from?  We choose the test
cases (more later in the course on how to choose them).  We determine
the expected outputs from the problem statement (not the program!).
We get the actual outputs from DrScheme by running the program.

Note that we do NOT need the program to determine the test cases or
the expected outputs.  We can determine those from the header and the
problem statement.  You should always develop test cases and their
expected outputs before you write the program.  This has two benefits: 
first, figuring out the expected outputs helps you understand the
problem: if you can't determine the expected outputs, you don't
understand the problem and you can't write the program.  Second,
determining the expected outputs before you write the program saves
you from the temptation of using the program to calculate the expected 
outputs (which would defeat the purpose); experience shows that this
temptation can be hard to avoid in practice.

We've now developed a sequence of steps (a recipe) for developing
programs: 

1. Write a contract, header, and purpose
2. Develop examples
3. Write the program body
4. Test the program

A new problem: Extra pizza calories must be worked-off through
exercise.  If you eat no more than two slices, you need to do 1 hour
of exercise.  Three to five slices require 1 1/2 hours of exercise.
More than 5 slices requires 3 hours of exercise.  Write a program
work-out that calculates how many hours of exercise you need for a
given number of slices.

Start with contract, purpose, and header

; work-out : num -> num
; Purpose : calculate hours of exercise needed to work off
;   s slices of pizza 
(define (work-out s) ...)

What test cases should I use?  Look at the problem statement.  We can
draw a number line and indicate where the divisions are between the
cases.  A square bracket means that the boundary number is included in
the interval; a parenthesis means that the boundary number is not
included in the interval.

	----------]|(------------]|(--------
                   2              5

We can use this diagram to choose test cases.  We need one test case
within each interval, plus one for each boundary number.  Let's choose 
the following test cases, and write down their expected values:

(work-out 1) = 1
(work-out 2) = 1
(work-out 4) = 3/2
(work-out 5) = 3/2
(work-out 7) = 3

Now we can write the body of the program.  For this problem, we must
determine whether some condition holds of the input.  What conditions
to we need?  s <= 2, 2 < s <= 5, and 5 < s.

We know how to express s <= 2 in Scheme.  We write (<= s 2).  What
kind of value does this return?  Either true or false.  These values,
true and false, are called boolean values.  

How do we express the two requirements 2 < s and s <= 5 in Scheme?  (< 2 
s), (<= s 5).  We want to form one condition from these Scheme
expressions.  Therefore, we need an operation that combines boolean
values.  We can use the operation called "and".  Thus, we express our
conditions in Scheme as shown in the table below.

         Condition                 Scheme
       ------------------------------------------------
	s <= 2			(<= s 2)
        2 < s and s <= 5	(and (< 2 s) (<= s 5))	
        5 < s			(< 5 s)

How do we write programs with conditions in Scheme?  We use an expression
called cond, which has the following format:

(cond [condition answer]
      ...
      [condition answer])

Return to work-out. What does the program body look like?  Start by writing
down the questions that distinguish the cases in the problem:

(define (work-out s)
  (cond [(<= s 2) ...]
        [(and (< 2 s) (<= s 5)) ...]
        [(< 5 s) ...])

What remains?  Fill in the answers in each case

(define (work-out s)
  (cond [(<= s 2) 1]
        [(and (< 2 s) (<= s 5)) 3/2]
        [(< 5 s) 3]))

Now, test the program on your chosen test cases.