If you have not already done so, please take the quiz before continuing with lab.

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.

  1. Function contract, purpose, header
  2. Function examples
  3. Function body
  4. 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.

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.

Simple data: Booleans, symbols

We've seen that DrScheme has

Numbers are old hat, so let's explore Booleans and symbols some.

Exercises: Using built-in data
  1. Try calling some the built-in functions like <=, =, and, or, not, symbol=? on various data. E.g., is 17 times 18 bigger than 256?
  2. What are the contracts of these built-in functions? Choose any two, and write down the contract. Verify your guess by asking the labby or by using DrScheme's Help Desk to look at the manual for the Beginning Student language.
  3. Practice writing a function which returns a Boolean: Following the design recipe, write within-two? which takes in two numbers m and n, and returns true if m-n and n-m are both less than 2. Otherwise, it should return false. (For learning purposes only, don't use abs, which computes the absolute value. Also, don't use cond or if.) Your examples can look like
         (boolean=? (within-two? 99.8 101) true)
         (boolean=? (within-two? 5 -5)     false)
                
Sample solution

Conditionals

At the end of Monday's class, we just started to talk about Booleans and 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 thing 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
  1. Copy-and-paste the definition of number-to-day. Use it on some example inputs.
  2. Use DrScheme's stepper on number-to-day applied to an input.
  3. 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
  1. 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, …

    1. What data type is appropriate to use for the distance?
    2. What data type is appropriate to use for the runners?
    3. Now following the design recipe, write the function.
  2. 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.

    1. Assuming these are the only six colors, what data type is appropriate to use to represent a color?
    2. Follow the design recipe to write the function.
    3. 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

  1. 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?
  2. In the running and color examples, we could have replaced the last clause question with else. Is this a good idea?
  3. 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.
  4. Once we've given this name to this type of data, what would the contract for your fastest-runner function be?
  5. How would we define a color type? How would we use it for the color complement and primary color tester?
  6. 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:

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.

Here's a full example for the colors.

Aside
What is step 3? We'll talk about that in class soon: it will just be a case of saying that defining your data alone gives you enough information to write the skeleton of any function which processes that data! (viz. take the header, and include step 6a). This is handy because you can write the skeleton just once, and then copy/paste it for every later function involving that data.