Developing conditional functions is more difficult than designing a plain function.
The key is to recognize that the problem statement lists cases and to identify the
different cases. To emphasize the importance of this idea, we introduce and
discuss a design recipe for designing conditional functions. The new recipe
introduces a new step, <#60773#><#3017#>DATA ANALYSIS<#3017#><#60773#>, which requires a programmer to
understand the different situations that the problem statement discusses. It also
modifies the Examples and the Body steps of the design recipe in
section~#secdesign1#3018>:
- Data Analysis:
- After determining that a problem statement deals with
distinct situations, we must identify all of them.
For numeric functions, a good strategy is to draw a number line and to
identify the intervals that correspond to a specific situation. Consider
the contract for the <#60774#><#3020#>interest-rate<#3020#><#60774#> function:
<#70721#>;; <#60775#><#3024#>interest-rate<#3024#> <#3025#>:<#3025#> <#3026#>number<#3026#> <#3027#><#3027#><#3028#>-;SPMgt;<#3028#><#3029#><#3029#> <#3030#>number<#3030#><#60775#><#70721#>
<#70722#>;; to determine the interest rate for the given <#60776#><#3031#>amount<#3031#> <#3032#>;SPMgt;=<#3032#> <#3033#>0<#3033#><#60776#><#70722#>
<#3034#>(define<#3034#> <#3035#>(interest-rate<#3035#> <#3036#>amount)<#3036#> <#3037#>...)<#3037#>
It inputs non-negative numbers and produces answers for three different
situations:
<#72385#>#tex2html_wrap72730#<#72385#>
For function that process booleans, the <#60790#><#3060#>cond<#3060#>-expression<#60790#> must
distinguish between exactly two situations: <#60791#><#3061#>true<#3061#><#60791#> and <#60792#><#3062#>false<#3062#><#60792#>.
We will soon encounter other forms of data that require case-based
reasoning.
- Function Examples:
- Our choice of examples account for the distinct
situations. At a minimum, we must develop one function example per
situation. If we characterized the situations as numeric intervals, the
examples should also include all borderline cases.
For our <#60793#><#3063#>interest-rate<#3063#><#60793#> function, we should use <#60794#><#3064#>0<#3064#><#60794#>,
<#60795#><#3065#>1000<#3065#><#60795#>, and <#60796#><#3066#>5000<#3066#><#60796#> as border-line cases. In addition, we
should pick numbers like <#60797#><#3067#>500<#3067#><#60797#>, <#60798#><#3068#>2000<#3068#><#60798#>, and <#60799#><#3069#>7000<#3069#><#60799#> to
test the interiors of the three intervals.
- The Function Body---Conditions:
- The function's body must consist of a
<#60800#><#3070#>cond<#3070#>-expression<#60800#> that has as many clauses as there are distinct
situations. This requirement immediately suggests the following body of our
solution:
<#3074#>(d<#3074#><#3075#>efine<#3075#> <#3076#>(interest-rate<#3076#> <#3077#>amount)<#3077#>
<#3078#>(c<#3078#><#3079#>ond<#3079#>
<#3080#>[<#3080#><#3081#>...<#3081#> <#3082#>...]<#3082#>
<#3083#>[<#3083#><#3084#>...<#3084#> <#3085#>...]<#3085#>
<#3086#>[<#3086#><#3087#>...<#3087#> <#3088#>...]<#3088#><#3089#>))<#3089#>
Next we must formulate the conditions that characterize each situation. The
conditions are claims about the function's parameters, expressed with Scheme's
relational operators or with our own functions.
The number line from our example translates into the following three
conditions:
- <#60801#><#3093#>(and<#3093#>\ <#3094#>(;SPMlt;=<#3094#>\ <#3095#>0<#3095#>\ <#3096#>amount)<#3096#>\ <#3097#>(;SPMlt;=<#3097#>\ <#3098#>amount<#3098#>\ <#3099#>1000))<#3099#><#60801#>
- <#60802#><#3100#>(and<#3100#>\ <#3101#>(;SPMlt;<#3101#>\ <#3102#>1000<#3102#>\ <#3103#>amount)<#3103#>\ <#3104#>(;SPMlt;=<#3104#>\ <#3105#>amount<#3105#>\ <#3106#>5000))<#3106#><#60802#>
- <#60803#><#3107#>(;SPMlt;<#3107#>\ <#3108#>5000<#3108#>\ <#3109#>amount)<#3109#><#60803#>
Adding these conditions to the function produces a better approximation of
the final definition:
<#3114#>(d<#3114#><#3115#>efine<#3115#> <#3116#>(interest-rate<#3116#> <#3117#>amount)<#3117#>
<#3118#>(c<#3118#><#3119#>ond<#3119#>
<#3120#>[<#3120#><#3121#>(and<#3121#> <#3122#>(;SPMlt;=<#3122#> <#3123#>0<#3123#> <#3124#>amount)<#3124#> <#3125#>(;SPMlt;=<#3125#> <#3126#>amount<#3126#> <#3127#>1000))<#3127#> <#3128#>...]<#3128#>
<#3129#>[<#3129#><#3130#>(and<#3130#> <#3131#>(;SPMlt;<#3131#> <#3132#>1000<#3132#> <#3133#>amount)<#3133#> <#3134#>(;SPMlt;=<#3134#> <#3135#>amount<#3135#> <#3136#>5000))<#3136#> <#3137#>...]<#3137#>
<#3138#>[<#3138#><#3139#>(;SPMgt;<#3139#> <#3140#>amount<#3140#> <#3141#>5000)<#3141#> <#3142#>...]<#3142#><#3143#>))<#3143#>
At this stage, a programmer should check that the chosen conditions
distinguish inputs in an appropriate manner. Specifically, if some input
belongs to a particular situation and <#60804#><#3146#>cond<#3146#><#60804#>-line, the preceding
conditions should evaluate to <#60805#><#3147#>false<#3147#><#60805#> and the condition of the line
should evaluate to <#60806#><#3148#>true<#3148#><#60806#>.
~<#70728#>If your students have difficulties with formulating conditions, have them do the following. First, replace the ...\ in the <#60807#><#3150#>cond<#3150#><#60807#>-clauses with <#60808#><#3151#>1<#3151#><#60808#>, <#60809#><#3152#>2<#3152#><#60809#>, <#60810#><#3153#>3<#3153#><#60810#>, <#3154#>etc.<#3154#>. Second, apply the function to the chosen examples in the <#3155#>Interactions<#3155#> window. An input for the first condition must produce the result <#60811#><#3156#>1<#3156#><#60811#>, and so on. Otherwise, we have developed the wrong conditions.<#70728#>
- The Function Body---Answers:
- Finally, it is time to determine what the
function should produce for each <#60812#><#3157#>cond<#3157#><#60812#>-clause. More concretely, we
consider each line in the <#60813#><#3158#>cond<#3158#>-expression<#60813#> separately, assuming
that the condition holds.
In our example, the results are directly specified by the problem
statement. They are <#60814#><#3159#>4.0<#3159#><#60814#>, <#60815#><#3160#>4.5<#3160#><#60815#>, and <#60816#><#3161#>5.0<#3161#><#60816#>. In more
complicated examples, we may have to determine an expression for each
<#60817#><#3162#>cond<#3162#><#60817#>-answer following the suggestion of our first design recipe.
<#3163#>Hint:<#3163#> \ If the answers for each <#60818#><#3164#>cond<#3164#><#60818#>-clause are complex, it is good
practice to develop one answer at a time. Assume that the condition
evaluates to <#60819#><#3165#>true<#3165#><#60819#>, and develop an answer using the parameters,
primitives, and other functions. Then apply the function to inputs that force
the evaluation of this new answer. It is legitimate to leave ``...'' in
place of the remaining answers.
- Simplification:
-
When the definition is complete and tested, a programmer might wish to
check whether the conditions can be simplified. In our example, we know
that <#60820#><#3166#>amount<#3166#><#60820#> is always greater than or equal to <#60821#><#3167#>0<#3167#><#60821#>, so the
first condition could be formulated as
<#3171#>(;SPMlt;=<#3171#> <#3172#>amount<#3172#> <#3173#>1000)<#3173#>
Furthermore, we know that <#60822#><#3176#>cond<#3176#>-expression<#60822#>s are evaluated
sequentially. That is, by the time the second condition is evaluated the
first one must have produced <#60823#><#3177#>false<#3177#><#60823#>. Hence, we know that the amount is
<#3178#>not<#3178#> less than or equal to <#60824#><#3179#>1000<#3179#><#60824#>, which makes the left
component of the second condition superfluous. The appropriately simplified
sketch of <#60825#><#3180#>interest-rate<#3180#><#60825#> is as follows:
<#3184#>(d<#3184#><#3185#>efine<#3185#> <#3186#>(interest-rate<#3186#> <#3187#>amount)<#3187#>
<#3188#>(c<#3188#><#3189#>ond<#3189#>
<#3190#>[<#3190#><#3191#>(;SPMlt;=<#3191#> <#3192#>amount<#3192#> <#3193#>1000)<#3193#> <#3194#>...]<#3194#>
<#3195#>[<#3195#><#3196#>(;SPMlt;=<#3196#> <#3197#>amount<#3197#> <#3198#>5000)<#3198#> <#3199#>...]<#3199#>
<#3200#>[<#3200#><#3201#>(;SPMgt;<#3201#> <#3202#>amount<#3202#> <#3203#>5000)<#3203#> <#3204#>...]<#3204#><#3205#>))<#3205#>
Figure~#figdesign2#3209> summarizes these suggestions on the design of conditional
functions. Read it in conjunction with figure~#figdesign1#3210> and compare the two
rows for ``Body.'' Re-read the table when designing a conditional function!
#tabular3212#
<#3271#>Figure: Designing the body of a conditional program<#3271#>
<#60828#>(Use with the recipe in figure~#figdesign1#3273> (pg.~#figdesign1#3274>))<#60828#>
<#3277#>Exercise 4.4.1<#3277#>
Develop the function <#60829#><#3279#>interest<#3279#><#60829#>. Like <#60830#><#3280#>interest-rate<#3280#><#60830#>, it
consumes a deposit amount. Instead of the rate, it produces the actual
amount of interest that the money earns in a year. The bank pays a flat 4
for deposits of up to $1,000, a flat 4.5 per year for deposits of up to
$5,000, and a flat 5 for deposits of more than
$5,000.~ Solution<#60831#><#60831#>
<#3286#>Exercise 4.4.2<#3286#>
Develop the function <#60832#><#3288#>tax<#3288#><#60832#>, which consumes the grosspay and produces
the amount of tax owed. For a grosspay of $240 or less, the tax is 0; for
over $240 and $480 or less, the tax rate is 15; and for any pay over
$480, the tax rate is 28.
Also develop <#60833#><#3289#>netpay<#3289#><#60833#>. The function determines the netpay of an
employee from the number of hours worked. Assume the hourly pay rate is
$12.
<#3290#>Hint:<#3290#> \ Remember to develop auxiliary functions when a definition becomes too
large or too complex to manage.~ Solution<#60834#><#60834#>
<#3296#>Exercise 4.4.3<#3296#>
Some credit card companies pay back a small portion of the charges a customer
makes during the year. One particular company follows the policy to return
- .25 for the first $500 of charges,
- .50 for the next $1000 (that is, the portion between $500 and $1500),
- .75 for the next $1000 (that is, the portion between $1500 and $2500),
- and 1.0 for everything above $2500.
Thus, a customer who charges $400 a year receives $1.00, which is #tex2html_wrap_inline72684#, and one who charges $1,400 a year receives $5.75,
which Is #tex2html_wrap_inline72686# for the first $500 and #tex2html_wrap_inline72688# for the next $900.
Determine by hand the pay-backs for a customer who charged $2000 and one
who charged $2600.
Define the function <#60835#><#3300#>pay-back<#3300#><#60835#>, which consumes a charge amount
and computes the corresponding pay-back amount.~ Solution<#60836#><#60836#>
<#3306#>Exercise 4.4.4<#3306#>
An equation is a claim about numbers; a quadratic equation is a special
kind of equation. All quadratic equations (in one variable) have the
following general shape:
#displaymath72690#
In a specific equation, a, b and c are replaced by numbers, as in
#displaymath72698#
or
#displaymath72700#
The variable x represents the unknown.
Depending on the value of <#60837#><#3308#>x<#3308#><#60837#>, the two sides of the equation
evaluate to the same value (see exercise~#exeqs#3309>). If the two sides
are equal, the claim is true; otherwise it is false. A number that makes
the claim true is a <#3310#>solution<#3310#>. The first equation has one solution,
-1, as we can easily check:
#displaymath72706#
The second equation has two solutions: +1 and -1.
The number of solutions for a quadratic equation depends on the values of a,
b, and c. If the coefficient a is 0, we say the equation is <#3311#>degenerate<#3311#> and do not consider how many solutions it has. Assuming a is not
0, the equation has
- two solutions if #tex2html_wrap_inline72724#,
- one solution if #tex2html_wrap_inline72726#, and
- no solution if #tex2html_wrap_inline72728#.
To distinguish this case from the degenerate one, we sometimes use the phrase
``<#3314#>proper<#3314#>'' quadratic equation.
Develop the function <#60838#><#3315#>how-many<#3315#><#60838#>, which consumes the coefficients
<#60839#><#3316#>a<#3316#><#60839#>, <#60840#><#3317#>b<#3317#><#60840#>, and <#60841#><#3318#>c<#3318#><#60841#> of a proper quadratic equation and
determines how many solutions the equation has:
<#3323#>(how-many<#3323#> <#3324#>1<#3324#> <#3325#>0<#3325#> <#3326#>-1)<#3326#> <#3327#>=<#3327#> <#3328#>2<#3328#>
<#3329#>(how-many<#3329#> <#3330#>2<#3330#> <#3331#>4<#3331#> <#3332#>2)<#3332#> <#3333#>=<#3333#> <#3334#>1<#3334#>
Make up additional examples. First determine the number of solutions by
hand, then with DrScheme.
How would the function change if we didn't assume the equation was
proper?~ Solution<#60842#><#60842#>