Comp210 Lecture # 2    Fall 2002

Remember, go to your preferred lab today/tomorrow and find a homework partner!

On Being Lazy...

The pizza industry is a tough biz; it's hard to stand out from the competition. [Not actually true in Houston, but we'll ignore that.] What if we want to switch to hexagonal pizzas? What code needs to be changed? Well, certainly we change dough-area, to reflect the formula for area of a hexagon ("radius" now means to dist-from-center-to-middle-of-side). But what about pizza-topping-area? No change needed! (Imagine that we'd used the weird pi/4*d^2... version -- we'd be wading through a long list of various pizza functions, and trying to figure out which were dependent on being a circle.) Thus, by re-using dough-area, we don't have to re-write other functions!

The Second Law of Programming: Re-use existing code.

We call this a "single point of control". Other uses of this (very general management) principle:

  • the same registrar information being replicated in the general announcements, the newspaper handout, departmental web pages, and advising handouts in o-week packets. Each time these are collected from a different source, you run the risk of contradictary material, and if you update one rule you need to remember to update all of the above.
  • Consider a set of web pages which say "page maintained by jane@rice.edu". What happens when jane moves on, and hugo takes her place: Do you go through and replace *each* occurrence of "jane" with "hugo"? But jane might have many other mentions on other web pages! A central point of control is saying "contact webmaster@rice.edu"; this email can be redirected as the job changes.

If you ever find yourself writing the same code to do the same thing (in this case, squaring a radius and multiplying by pi), you should stop and think about how to re-use the common code.

How does code re-use relate to the notion of expressing a task in terms of abstractions?

 

Brass Tacks

So, we've decided to attempt to model computational processes in terms of mathematical functions....So how do we do that?

Let's look at two ways of writing mathematical expressions:

expression (arithmetic "in-fix" notation) expression (Scheme "pre-fix" notation) value
2 + 2 (+ 2 2) 4
7 / 3 (/ 7 3) 2 1/3
3 * (((4 * 5))) (* 3 (* 4 5)) 60
3 + (4 * 5) (+ 3 (* 4 5)) 23
(3 + 4) * 5 (* (+ 3 4) 5) 35
  ___
-/16
(sqrt 16) 4
  ______________
-/ 7*7 + 24*24
(sqrt (+ (* 7 7) (* 24 24))) 25
         _____________
 -5 +  -/ 5*5 - 4*1*7
----------------------
       2*1
???
let owes(p) = 12*(p/8)
(define (owes p)
  (* 12 (/ p 8)))
n/a
let dough-area(r) = pi*r^2
(define (dough-area r)
  (* pi (* r r)))
n/a
let pizza-topping-area(d) = dough-area(d/2 - 1)
(define (pizza-topping-area d)
  (dough-area (- (/ d 2) 1)))
n/a

What's the advantage of pre-fix notation over in-fix notation?

Scheme Expression Syntax

( [function name] [argument1] [argument2] [argument3] [etc.] )

Could the function name or arguments be the result of an expression? ....Why not?

How would one create an arbitrarily complex expression?

Defining a function in Scheme

Defining a function in Scheme requires the use of a special operation called "define" (duh!). The syntax looks like that of a regular expression but on closer examination one sees that it is not quite the same:

(define ( [function name] [argument1] [argument2] [etc.]) ( [expr. using args] ) )

The define operation doesn't evaluate its two arguments but instead uses them the

  1. define the name of the function and what inputs it expects (the first argument above)
  2. define the abstract expression that will be evaluated using the values given as the inputs (the second argument above)

 

 

To do before next lecture:
Start up DrScheme, and double-check your expression for

         _____________
 -5 +  -/ 5*5 - 4*1*7
----------------------
       2*1

(/ (+ -5 (sqrt (- (* 5 5) (* 4 1 7)))) (* 2 1))

Prof. Cooper wrote up a nice discussion covering the material from these last two lectures. I won't attempt to duplicate his fine work here, but merely and shamelessly utilize it. Click here to see his lecture from the spring of 2002. Prof. Barland also wrote some good stuff, most of which I used with his permission here (really!) but if you want to see the original, go here.

But is that all there is to it?

So, all we've got to do is to define up a bunch of functions and we can compute anything we want, right?

Remember, a program is an expression of how we view and understand a problem. Do our functions completely define everything we wish to express about our tasks?

Or are there things that got left out?

Consider the following functions:

What's missing here?

 

 

 

Contracts

The code to define a function does not specify all the requirements and results of the function.

We need to specify, in comments,

  1. What are the expected inputs:

    1. how many values

    2. what types of values (integers, floating point, characters, strings, arrays, lists, etc)

    3. what each value represents

    4. what range of acceptible values

  2. What is the expected output:

    1. what type of value

    2. what the returned value represents ("purpose")

Note: this discussion is about the notion of contracts in general. In this course we will use the word "contract" to refer everything except the "purpose" in the general, overall notion of a contract discussed above.

Scheme Syntax Note:

A comment in Scheme is the rest of any line, starting at a semicolon ";" or is in-between the following pair: #| ... |#

For example

; This is my o-so-fancy function
(define (f x) (random x)) ; x determines the range of random numbers
#|
This code doesn't work
(define (f x) (f x))
|#

 

A complete function definition MUST specify its contract and purpose!

 

Remember: Contracts in Scheme are only in the form of comments. Scheme does not enforce the following of the contracts in any way!

(Wouldn't it be nice if the code enforced the contracts? Sorry--that's next semester!)

Suggestion: Use names that mean something, such as get_last_element or positive_quadratic_root rather than func1 and func2 (remember the first law of programming?).

Summary

  1. 2nd law of programming -- code reuse
  2. Scheme expression and function definition syntax
  3. Contracts

 

©2002 Stephen Wong