cond
Original Author: Dr. John Greiner
In this lab, you'll practice with some of the things introduced in class, including functional decomposition, and functions on Booleans and symbols. You'll explore conditionals in greater depth. Based upon this work, we'll revise the design recipe.
Instructions for students & labbies: Students should read and use DrScheme on the exercises at their own pace, while labbies wander among the students, answering questions, bringing the more important ones to the lab's attention. Students shouldn't look at the sample solutions until finishing the exercise. Depending on their pace, students won't necessarily have time to finish all exercises. That's perfectly OK, in which case, students should skip ahead during lab to do at least one exercise in each section. For the last 25 minutes or so, have a group discussion on the points listed and consider the modified design recipe.
Design Recipe Reminder
Let's quickly recall the design recipe (so far). We'll fill in some new steps during this lab, so we'll start with some funny numbering.
- Function contract, purpose, header
- Function examples
- Function body
- Function testing
Programming is not tinkering on a program until it passes some tests. That approach leaves people wandering, and provides little confidence in the program correctness, and none in its maintainability. There is Process in creating a program.
Functional Decomposition
In general, it is better to write your programs as a collection of small functions, rather than one giant function.
- Each function is simpler. Each is easier to understand, write, test, and debug.
- Code is re-used. Each computation is written once, instead of multiple times -- a single point of control. Thus, if we need to change/fix, say, the computation of the average of a bunch of numbers, we only need to change one piece of code. This generally leads to shorter code with fewer mistakes.
Exercise: Function decomposition
|
Following the design template, rewrite the following (correct) code in better style.
; hypotenuse : real real -> positive-real
; Returns the length of the hypotenuse of a right triangle having
; the given height and width.
(define (hypotenuse height width)
(sqrt (+ (* height height) (* width width))))
'hypotenuse_examples
(= (hypotenuse 0 0) 0)
(= (hypotenuse 3 4) 5)
(= (hypotenuse 6 8) 10)
|
| Sample solution |
Conditionals
In this section, we'll go into explore conditionals more. A
cond expression looks like the following:
(cond
[(symbol=? 'Sam 'Sandy) 'wrongo]
[(symbol=? 'Sam 'Sam) 'yep]
[else 'hmmmm])
This starts with the cond keyword. It then has a series of "clauses", here, three of them. Here, each clause is grouped with brackets, but you can use parentheses instead, too. Each clause has a question and an answer. The whole cond expression evaluates to the answer of the first true question. The last clause's question can be else, which is always true.
Of course, writing code like the above, where we test the equality of two known things, is kind of silly. A more realistic example would be the following:
; number-to-day : nat in 1...7 -> 'symbol
; Purpose: Returns the name of the day corresponding to the
; given number, assuming that the week starts with Monday.
(define (number-to-day num)
(cond
[(= 1 num) 'Monday]
[(= 2 num) 'Tuesday]
[(= 3 num) 'Wednesday]
[(= 4 num) 'Thursday]
[(= 5 num) 'Friday]
[(= 6 num) 'Saturday]
[(= 7 num) 'Sunday]))
Exercises: Using cond
- Copy-and-paste the definition of
number-to-day. Use it on some example inputs.
- Use DrScheme's stepper on
number-to-day applied to an input.
- Write our previous example
within-two? again, but use cond this time. Your comments and examples from the first version should remain unchanged, so you can just copy-and-paste them.
|
Sample solution for within-two?. |
The previous example is one way we use conditionals. The following two have a slightly different flavor that we'll discuss.
Exercise: Using cases
-
We'd like to keep track of who the fastest runner is for the Rice track team. Of course, who is fastest depends entirely on what distance you're interested in. Let's assume we're interested in all distances, not just those used in track meets.
Marianne is the team's best sprinter, consistently the best for the standard 100m and 200m distances, and generally for anything under 300m. Becca is the outstanding middle-distance runner for the standard 400m and 800m distances, but also from 300m to just under 1000m. Suzy is best for the long distances, from 1000m to just under 10000m. Finally, marathon specialist Yvonne outlasts everyone for races 10000m and up.
Write a function fastest-runner so that by plugging in a distance, the function returns the fastest runner. But first, …
- What data type is appropriate to use for the distance?
- What data type is appropriate to use for the runners?
- Now following the design recipe, write the function.
-
Write a function which returns the complementary color of its input. Asking our color expert, we find out that red and green are complements, as are orange and blue, and yellow and violet.
- Assuming these are the only six colors, what data type is appropriate to use to represent a color?
- Follow the design recipe to write the function.
- If you have time, follow the design recipe to write a function that returns whether a color is a primary color, i.e., red, yellow, or blue. Again, assume these are the only six colors. (Hmm, isn't that going to be similar in structure?)
|
Sample solutions: fastest-runner, colors. |
Discussion
-
In fastest-runner, we could have shortened the sample solution by eliminating the first part of the and in each of the 2nd and 3rd cases. Why is this a bad idea?
- In the running and color examples, we could have replaced the last clause question with
else. Is this a good idea?
- Is there anything in common about the fastest runner and color examples? Yes! We find there are often several times that we'd like to create or our categories or types of data. E.g.,
; A running-distance is one of
; - an integer in [0,300)
; - an integer in [300,1000)
; - an integer in [1000,10000)
; - an integer in [10000,+inf.0)
Aside
| This is "half-open interval" notation, standard for mathematicians: the square-bracket means include the number in the interval, and the round-bracket means exclude the number from the interval. These are not meant to be scheme-parentheses. We'll use "+inf.0" to mean positive-infinity for now. |
This data definition better reflects our thinking of the program.
We might want to use this user-defined type several times. E.g., to write functions for female runners, male runners, Junior Olympics, whatever.
- Once we've given this name to this type of data, what would the contract for your
fastest-runner function be?
- How would we define a color type? How would we use it for the color complement and primary color tester?
- There are several ways to write the primary color tester. What are their tradeoffs?
In summary, this "x, y, or z" structure is a very common way to define data, and you see that the code reflects this way of thinking about the data: There is one cond branch for each branch of data.
Stepping Back: Recipe Redux
What was involved in the preceding examples? We realized that when approaching a problem, the
first thing we do, is decide how our program will represent data, in the problem we're modeling. Moreover, we've seen two common situations so far:
- The data is just one of the built-in data types. Any particular function dealing with that data will use the corresponding built-in functions.
- The data is of the form "x, y, or z". Any particular function dealing with that data will use a cond, with one branch for each type of data.
- ... There will be a third case next week. ...
Our code mirrors our data, which in turn mirrors how we conceive of the problem. This is no coincidence. More than half the work of writing a good program lies in choosing good data definitions. We'll see how the shape of the data often guides you to a bug-free solution.
We'll close by updating our design recipe.
- new Data definition: How, exactly, do you represent the problem in terms of Scheme data?
- new Data examples.
- Function contract, purpose, header.
- Function examples: Use your previously-written data examples.
-
Function body.
- new If your data has various cases, include a
cond -- one branch per case of data. (You can write all the cond questions first, to distinguish the data, and come back later to worry about the answer for a particular function.)
- Function testing: Use your previously-written examples.
Access Permissions