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)))) |
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.
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:
Profit is the difference between revenue and costs.
The revenue is exclusively generated by the sale of tickets. It is the product of ticket price and number of attendees.
The costs consist of two parts: a fixed part ($180) and a variable part that depends on the number of attendees.
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 giventicket-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, giventicket-price
(define (revenue ticket-price) ...) ;;cost : number -> number
;; to compute the costs, giventicket-price
(define (cost ticket-price) ...) ;;attendees : number -> number
;; to compute the number of attendees, giventicket-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.
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
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
. 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 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 , 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
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
|
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.