Experiments in Java


Session J5: Control Structures for Repetition

In the previous laboratory session, we developed a number of methods for the SimpleDate class, progressing from simple methods to more advanced methods. In this laboratory, we will continue with our exploration of the SimpleDate class. We will begin by looking at a dayOfYear method which determines which day of the year it is. From that method, we will develop a daysUntil method that determines the number of days until a particular date. Using that method, in the experiments you will develop a dayOfWeek method that determines the day of the week a date falls on.

Repetition

How do we determine what day of the year a particular date is? We can determine the number of days between the first day of the year and the first day of the month, and then add which day of the month it is. For example, consider February 3. The zeroth day of February is 31 days from the beginning of the year. February 3 comes three days later, so February 3 is 34 days from the beginning of the year.

Unfortunately, the computation for later months is not so easy. For example, how many days is August 1 from the beginning of the year? It may be possible to precompute all of those numbers, as in

  /**
   * Compute the position of this day within the current year.
   */
  public int dayOfYear() {
    int to_start_of_month;
    switch (this.month) {
      case 1:  // January
        to_start_of_month = 0;
        break;
      case 2:  // February
        to_start_of_month = 31;
        break;
      case 3:  // March
        to_start_of_month = 59; // We'll deal with leap years later in this function.
        break;
      ...
    } // switch(this.month)
    // Handle leap years
    if (this.isLeapYear() && this.month > 2) {
       to_start_of_month = to_start_of_month + 1;
    } // After February in a leap year
    // Add the days in this month
    return to_start_of_month + this.day;
  } // dayOfYear()

However, the work precomputing the position of the beginning of each month from the beginning of the year is painful and prone to simple errors in calculation if done by hand. Hence, we'd prefer to have the computer do such computation. How do we compute the number of days until the first of month m? We sum the number of days in each of the previous months. Algorithmically, we might write

// Compute the number of days until the start of month m
daysUntilStartOfMonth(m)
  sum the number of days in each month, p, that precedes m
  if it's a leap year and after February, add 1
  return the result

The sum command in that algorithm is vague and is not included in most programming languages. Hence, we must expand it to work out the details. How do we compute the sum? By explicitly adding the days in each prior month.

// Compute the number of days until the start of month m
daysUntilStartOfMonth(m)
  days = 0
  for each month, p, that precedes m
    add the number of days in month p to days
  end for
  if it's a leap year and after February, add 1 to days
  return days

The ``for each month'' is also vague. It may be better to make it explicit by using a counter.

// Compute the number of days until the start of month m
daysUntilStartOfMonth(m)
  days = 0
  p = 1
  while p < m
    add the number of days in month p to days
    add 1 to p
  end for
  if it's a leap year and after February, add 1 to days
  return days

Rewriting this in Java-like syntax, we get

  //**
   * Compute the number of days until the start of the month.
   */
  public int daysUntilStartOfMonth(int m) {
    int prev = 1; // A previous month
    int days = 0; // Total number of days
   
    // Sum the days in the previous months.  We assume that the
    // daysInMonth method handles leap years
    // correctly.
    while (prev < m) {
      days = days + this.daysInMonth(prev);
      prev = prev + 1;
    } // while

    // And that's it.
    return days;
  } // daysUntilStartOfMonth(int)

While loops

In fact, this is legal Java. Java provides a while statement with the following form.

while (test) {
  body
} // while

The test is executed. If it fails, the while loop terminates and control passes to the subsequent statement. If the test holds, then the statements in the body are executed. After the body is executed, the test is done again. The test and body are repeatedly executed until the test no longer holds.

Note that the test is done once per repetition, which means that the test need hold only at the beginning of the body. If, during the body, the test would no longer hold, execution of the body still continues until the end.

In Experiment J5.1 you will experiment with some basic looping examples.

Detour: Overloading revisited

Wait! Something seems wrong with the code above. It uses a daysInMonth(int) method, and you may recall that we designed daysInMonth to take no parameters. What do we do? Once again, this is an instance of overloading method names: providing multiple methods with the same name, but different types of parameters.

Using overloading, we might extend our old SimpleDate class with the new, parameterized daysInMonth. We can also have the old daysInMonth use the new one, as in

  /**
   * Compute the number of days in the current month.
   */
  public int daysInMonth() {
    return this.daysInMonth(this.month);
  } daysInMonth()

  /**
   * Compute the number of days in month m.
   */
  public int daysInMonth(int m) {
    switch (m) {
      case 1:
      case 3:
      ...
        return 31;
        break;
      case 2:
        if (this.isLeapYear()) {
          return 29;
        } // February in a leap year
        else {
          return 28;
        } // February, but not a leap year
        break;
      ...
    } // switch
  } // daysInMonth(int)

In Experiment J5.2 you will consider how to use loops to count days in the year.

for loops

Is the while loop the only looping mechanism that Java provides? No. Because so many algorithms naturally include a section that reads for every value of n between a and b do ... that Java includes a for control structure to make it more convenient to express such algorithms.

The for loop has the following form

for (initialization; test; increment) {
  body
} // for

In effect, this is a shorthand for the following while loop.

initialization;
while (test) {
  body;
  increment;
} // while

That is, a for loop begins by executing the initialization portion. Traditionally, this sets the initial value for a counter variable. Next, it executes the test. If the test fails, the loop terminates and control moves on to the next statement. If the test succeeds, the body is executed. After the body is done, the increment is executed. The test is done again, and the cyclic process continues.

For example, we might rewrite our daysUntilStartOfMonth method as

  //**
   * Compute the number of days until the start of the month.
   */
  public int daysUntilStartOfMonth(int m) {
    int prev = 1; // A previous month
    int days = 0; // Total number of days
   
    // Sum the days in the previous months
    for (prev = 1; prev < m; prev = prev + 1) {
      days = days + this.daysInMonth(prev);
    } // for
    
    // Deal with leap years
    if (this.isLeapYear() && m > 2) {
       days = days + 1;
    } // After February in a leap year

    // And that's it.
    return days;
  } // daysUntilStartOfMonth(int)

In Experiment J5.3 you will consider some simple examples of for loops.

Detour: Arithmetic shorthand

As you read other people's Java code (or C code or C++ code or ...), you will find many shorthand expressions for common arithmetic expressions. For example, it is so typical to add one to a variable that Java includes a ++ operator that does just that. In particular,

    ++i;

is the same as

    i = i + 1;

Hence, you will often see for loops that look like

    for (i = 0; i < n; ++i) 

The prefix ++ (also called preincrement) differs from the longer form in that it can be more easily used as part of larger expressions. For example,

    j = ++i + 3; 

represents ``Add 1 to i. Add 3 to the updated i, and store the result in j.''

There is also a postfix ++ (also called postincrement) that adds 1 to the argument, but returns the old value. For example,

    j = i++ + 3; 

represents ``Add 1 to i; add 3 to the old value of i; store the result in j''.

As you might guess, there are also prefix and postfix -- operators (predecrement and postdecrement), with the intended meaning of decrementing the corresponding variable.

Finally, Java also provides a += assignment which has the affect of adding the right-hand-side to the left-hand-side. For example,

k += 5;

represents ``Add 5 to k''.

In Experiment J5.4 you will consider Java's various arithmetic shorthands.

Loops and applets

This section is optional and is intended for classes emphasizing applets or pursuing a simultaneous discussion of applications and applets.

Loops have many applications for applets, particularly with regards to drawing regular and repetitious figures, such as sequences of images. In Experiment J5.5, you will use for loops to create a rainbow text effect. In Experiment J5.6, you will track down errors in an applet designed to draw a regular grid using a for loop.

In Experiment J5.7, you will attempt a more complicated task: showing the positions of a bouncing ball. In order to make the ball bounce, you need to be able to determine the area available to the applet.

You may have noted that the dimensions (width and height) of the area reserved for an applet are set within the HTML files that loads the applet and not within the applet itself. Clearly, it is possible to load the same applet with different dimensions and different pages may therefore choose different dimensions for the same applet. For many applets, it becomes important for the applet designer to be able to determine these dimensions. This is particularly true of our bouncing ball applet, in which the ball must change direction

Fortunately, Java provides a mechanism for the applet author to determine the dimensions given for the applet. You can call this.getSize() to obtain a java.awt.Dimension object. You can then get the width of that object by referring to its width and height fields.

For example, you might write the following to set the right and bottom edges of the area allocated to the applet. Note that since the area begins at (0,0), the width of the applet gives the horizontal offset of the right edge, and the height of the applet gives the vertical offset of the bottom edge.

    int right;  // The right edge of the area allocated to the applet.
    int bottom; // The bottom edge of the area allocated to the applet.
    Dimension dim = this.getSize();
    right = dim.width;
    bottom = dim.height;

Now, how do you make the ball bounce? We'll explore that issue in Experiment J5.7.


Copyright (c) 1998 Samuel A. Rebelsky. All rights reserved.

Source text last modified Mon Oct 25 16:21:05 1999.

This page generated on Tue Oct 26 15:38:16 1999 by Siteweaver.

Contact our webmaster at rebelsky@math.grin.edu