Section 3

Programs are Function Plus Variable Definitions

In general, a program consists not just of one, but of many definitions. The area-of-ring program, for example, consists of two definitions: the one for area-of-ring and another one for area-of-disk. We refer to both as FUNCTION DEFINITIONs and, using mathematical terminology in a loose way, say that the program is COMPOSED of several functions. Because the first one, area-of-ring, is the function we really wish to use, we refer to it as the MAIN FUNCTION; the second one, area-of-disk, is an AUXILIARY FUNCTION, occasionally also called HELPER FUNCTION.

The use of auxiliary functions makes the design process manageable and renders programs readable. Compare the following two versions of area-of-ring:

(define (area-of-ring outer inner) 
  (- (area-of-disk outer)
     (area-of-disk inner)))
(define (area-of-ring outer inner) 
  (- (* 3.14 (* outer outer))
     (* 3.14 (* inner inner))))
The definition on the left composes auxiliary functions. Designing it helped us break up the original problem into smaller, more easily solvable problems. Reading it reminds us of our reasoning that the area is the difference between the area of the full disk and the area of the hole. In contrast, the definition on the right requires a reader to reconstruct the idea that the two subexpressions compute the area of two disks. Furthermore, we would have had to produce the right definition in one monolithic block, without benefit of dividing the problem-solving process into smaller steps.

For a small program such as area-of-ring, the differences between the two styles are minor. For large programs, however, using auxiliary functions is not an option but a necessity. That is, even if we are asked to write a single program, we should consider breaking it up into several small programs and COMPOSING them as needed. Although we are not yet in a position to develop truly large programs, we can still get a feeling for the idea by developing two versions in parallel.

The first subsection contrasts the two development styles with an example from the business domain. It demonstrates how breaking up a program into several function definitions can greatly increase our confidence in the correctness of the overall program. The second subsection introduces the concept of a variable definition, which is an additional important ingredient for the development of programs. The last subsection proposes some exercises.

3.1  Composing Functions

Consider the following problem:

Imagine the owner of a movie theater who has complete freedom in setting ticket prices. The more he charges, the fewer the people who can afford tickets. In a recent experiment the owner determined a precise relationship between the price of a ticket and average attendance. At a price of $5.00 per ticket, 120 people attend a performance. Decreasing the price by a dime ($.10) increases attendance by 15. Unfortunately, the increased attendance also comes at an increased cost. Every performance costs the owner $180. Each attendee costs another four cents ($0.04). The owner would like to know the exact relationship between profit and ticket price so that he can determine the price at which he can make the highest profit.
While the task is clear, how to go about it is not. All we can say at this point is that several quantities depend on each other.

When we are confronted with such a situation, it is best to tease out the various dependencies one at a time:

  1. Profit is the difference between revenue and costs.

  2. The revenue is exclusively generated by the sale of tickets. It is the product of ticket price and number of attendees.

  3. The costs consist of two parts: a fixed part ($180) and a variable part that depends on the number of attendees.

  4. Finally, the problem statement also specifies how the number of attendees depends on the ticket price.

Let's formulate a function for each of these dependencies; after all, functions compute how quantities depend on each other.

We start with contracts, headers, and purpose statements. Here is the one for profit:

;; profit : number  ->  number
;; to compute the profit as the difference between revenue and costs
;; at some given ticket-price
(define (profit ticket-price) ...)

It depends on the ticket price because both revenue and cost depend on the ticket price. Here are the remaining three:

;; revenue : number  ->  number
;; to compute the revenue, given ticket-price 
(define (revenue ticket-price) ...)

;; cost : number  ->  number
;; to compute the costs, given ticket-price
(define (cost ticket-price) ...)

;; attendees : number  ->  number
;; to compute the number of attendees, given ticket-price
(define (attendees ticket-price) ...)

Each purpose statement is a rough transliteration of some part of the problem statement.

Exercise 3.1.1.   The next step is to make up examples for each of the functions. Determine how many attendees can afford a show at a ticket price of $3.00, $4.00, and $5.00. Use the examples to formulate a general rule that shows how to compute the number of attendees from the ticket price. Make up more examples if needed.    Solution

Exercise 3.1.2.   Use the results of exercise 3.1.1 to determine how much it costs to run a show at $3.00, $4.00, and $5.00. Also determine how much revenue each show produces at those prices. Finally, figure out how much profit the monopolistic movie owner can make with each show. Which is the best price (of these three) for maximizing the profit?    Solution

Once we have written down the basic material about our functions and calculated out several examples, we can replace the ``...'' with Scheme expressions. The left column of figure 5 contains complete definitions of all four functions. The profit function computes its result as the difference between the result of revenue and cost, just as the problem analysis and purpose statement suggest. The computation of both depends on ticket-price, which is what the applications say. To compute the revenue, we first compute the number of attendees for the given ticket-price and multiply it with ticket-price. Similarly, to compute the cost we add the fixed portion of the cost to the variable part, which is the product of the number of attendees and 0.04 (four cents). Finally, the computation of the number of attendees also follows the problem statement. The base attendance at a price of five dollars is 120, and for each 10 cents less than five dollars, 15 more attendees show up.

;; How to design a program 
(define (profit ticket-price)
  (- (revenue ticket-price)
     (cost ticket-price)))

(define (revenue ticket-price)
  (*  (attendees ticket-price) ticket-price))

(define (cost ticket-price)
  (+ 180 
     (* .04 (attendees ticket-price))))

(define (attendees ticket-price)
  (+ 120
     (* (/ 15 .10) (- 5.00 ticket-price))))
    
;; How not to design a program 
(define (profit price)
  (- (* (+ 120
	   (* (/ 15 .10)
	      (- 5.00 price)))
	price)
     (+ 180 
	(* .04
	   (+ 120
	      (* (/ 15 .10)
		 (- 5.00 price)))))))




Figure 5:  Two ways to express the profit program

Instead of developing a function per dependency in the problem statement, we could have tried to express the relationship between the ticket price and the owner's profit in a single function. The right column in figure 5 shows the most straightforward way of doing so. And indeed, it is easy to check that the two profit programs in figure 5 produce the same profit when given the same ticket price. Still, it is also obvious that while the arrangement on the left conveys the intention behind the program directly, the program on the right is nearly impossible to understand. Worse, if we are asked to modify some aspect of the program, say, the relationship between the number of attendees and the price of the ticket, we can do this for the left column in a small amount of time, but we need to spend a much longer time for the right one.

Based on our experience, we thus formulate the first and most important guideline of programming:

Guideline on Auxiliary Functions

Formulate auxiliary function definitions for every dependency between quantities mentioned in the problem statement or discovered with example calculations.

Sometimes we will find that some of the required functions are already available as programs for other problems. Indeed, we have already encountered such an example: area-of-disk. At other times, we will make a list of functions and develop each one separately. We may then find that some of the functions, such as attendees, are useful in several other definitions, leading to a network-like relationship among functions.

Exercise 3.1.3.   Determine the profit that the movie owner makes at $3.00, $4.00, and $5.00 using the program definitions in both columns. Make sure that the results are the same as those predicted in exercise 3.1.2.    Solution

Exercise 3.1.4.   After studying the cost structure of a show, the owner discovered several ways of lowering the cost. As a result of his improvements, he no longer has a fixed cost. He now simply pays $1.50 per attendee.

Modify both programs to reflect this change. When the programs are modified, test them again with ticket prices of $3.00, $4.00, and $5.00 and compare the results.    Solution

3.2  Variable Definitions

[../icons/plt.gif]
Defining
Variables

When a number occurs many times in our program(s), we should give it a name using a VARIABLE DEFINITION, which associates a name with a value. One example is 3.14, which we have used in place of [curriculum-Z-G-D-1.gif]. Here is how we could give this number a name:

(define PI 3.14)

Now, every time we refer to PI, DrScheme replaces it with 3.14.

Using a name for a constant makes it easier to replace it with a different value. Suppose our program contains the definition for PI, and we decide that we need a better approximation of [curriculum-Z-G-D-1.gif] for the entire program. By changing the definition to

(define PI 3.14159)

the improvement is used everywhere where we use PI. If we didn't have a name like PI for [curriculum-Z-G-D-1.gif], we would have to find and all instances of 3.14 in the program and replace them with 3.14159.

Let us formulate this observation as our second guideline:

Guideline on Variable Definitions

Give names to frequently used constants and use the names instead of the constants in programs.

Initially, we won't use many variable definitions for constants, because our programs are small. But, as we learn to write larger programs, we will make more use of variable definitions. As we will see, the ability to have a single point of control for changes is important for variable and function definitions.

Exercise 3.2.1.   Provide variable definitions for all constants that appear in the profit program of figure 5 and replace the constants with their names.    Solution

3.3  Finger Exercises on Composing Functions

[../icons/plt.gif]
Using ``...''

Exercise 3.3.1.   The United States uses the English system of (length) measurements. The rest of the world uses the metric system. So, people who travel abroad and companies that trade with foreign partners often need to convert English measurements to metric ones and vice versa.

Here is a table that shows the six major units of length measurements of the English system:12

English                 metric
1 inch = 2.54 cm
1 foot = 12 in.
1 yard = 3 ft.
1 rod = 5(1/2) yd.
1 furlong = 40 rd.
1 mile = 8 fl.

Develop the functions inches->cm, feet->inches, yards->feet, rods->yards, furlongs->rods, and miles->furlongs.

Then develop the functions feet->cm, yards->cm, rods->inches, and miles->feet.

Hint: Reuse functions as much as possible. Use variable definitions to specify constants.    Solution

Exercise 3.3.2.   Develop the program volume-cylinder. It consumes the radius of a cylinder's base disk and its height; it computes the volume of the cylinder.    Solution

Exercise 3.3.3.   Develop area-cylinder. The program consumes the radius of the cylinder's base disk and its height. Its result is the surface area of the cylinder.    Solution

Exercise 3.3.4.   Develop the function area-pipe. It computes the surface area of a pipe, which is an open cylinder. The program consumes three values: the pipe's inner radius, its length, and the thickness of its wall.

Develop two versions: a program that consists of a single definition and a program that consists of several function definitions. Which one evokes more confidence?    Solution

Exercise 3.3.5.   Develop the program height, which computes the height that a rocket reaches in a given amount of time. If the rocket accelerates at a constant rate g, it reaches a speed of g · t in t time units and a height of 1/2 * v * t where v is the speed at t.    Solution

Exercise 3.3.6.   Recall the program Fahrenheit->Celsius from exercise 2.2.1. The program consumes a temperature measured in Fahrenheit and produces the Celsius equivalent.

Develop the program Celsius->Fahrenheit, which consumes a temperature measured in Celsius and produces the Fahrenheit equivalent.

Now consider the function

;; I : number  ->  number
;; to convert a Fahrenheit temperature to Celsius and back 
(define (I f)
  (Celsius->Fahrenheit (Fahrenheit->Celsius f)))

Evaluate (I 32) by hand and using DrScheme's stepper. What does this suggest about the composition of the two functions?    Solution


12 See The World Book Encyclopedia 1993, Weights and Measurements.