int month:
for (month = 1; month <= 12; month++) { printCalendarMonth (month, year);
printf (“\n”);
} }
The PrintCalendarMonth function takes two arguments—the month and the year—since it requires both pieces of information. The argument month is required so that the function knows which month to display; year is required because the calendar for a particular month varies from year to year.
Implementing PrintCalendarMonth
Implementing printCalendarMonth is a little more difficult. The problem is to display a single month in a format that looks like this:
The first two lines of output are reasonable easy to handle. The only part that might seem hard is taking the numeric value month and translating it into its conventional name. This operation, however, turns out to be very easy for a reason that is well worth noting: you already have seen a function that performs precisely this operation. The function MonthName presented in the section on ―Functions that return nonnumeric values‖ earlier in this chapter is just what you need. Given MinthNmae, you can display the two header lines of the calendar using the statements:
printf(“ $%s %d\n”, MonthName (month), year);
printf(“ Su Mo Tu We Th Fr Sa\n”);
Now comes the interesting part. The rest of the monthly calendar consists of the integers between 1 and the number of days in the month. A for loop can handle this aspect of the task. The catch is that the formatting is tricky. For one thing, the month has to start on the correct day of the week. For another, after each Saturday, the output has to continue at the beginning of the next line.
To solve the formatting problems, you will need to keep track of both the day of the week and the day of the month. How should the days of the week be represented? One approach is simply to number them. The most convenient numbering scheme, given the calendar layout, is to define Sunday to be 0, Monday to be 1, and so forth, up to Saturday, which is numbered 6. So that you could refer to the names of these days in the program, you might choose to define them as constants, as follows:
#define Sunday 0
#define Monday 1
February 1992
Su Mo Tu We Th Fr Sa 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
#define Tuesday 2
#define Wednessday 3
#define Thursday 4
#define Friday 5
#define Saturday 6
There is an advantage to starting the weekday numbering with 0: doing so means that you can implement the operation of cycling past the end of one week by using the remainder operator. If the variable weekday contains the integer corresponding to the current day of the week, the expression
(weekday + k ) % 7
indicates the day of the week that occurs k days late on. For example, if to day is a Friday (when weekday has the value 5), 10 days from today is a Monday, because the expression
(5 + 10) % 7
comes out to be 1, In particular, you can apply this formula to write the following statement, which corresponds to the idea of moving ahead to the next weekday:
weekday = (weekday + 1) % 7;
The more familiar expression
weekday++; The ++ operator will count past 6
is not appropriate here because weekday would eventually become 7,8,9, and so forth, which do not correspond to weekdays. By dividing by 7 and taking the remainder, you can ensure that the result is always between 0 and 6. When you use the reminder operation to confine the result of a calculation to a small cyclical range by taking a remainder, you are using a process that mathematicians call modular arithmetic. Modular arithmetic is extremely useful in programming; you will see several additional examples of its use throughout the text.
If you keep track of the weekday, writing the main loop inside the PrintCalendarMonth function is not difficult. The following code does the job:
for (day = 1; day <= nDays; day++_ { print(“ %2d”, day);
if (weekday == Saturday) printf (“\n”);
weekday = (weekday + 1) % 7;
}
This loop displays each number, keeps track of the weekday, and puts in the new line after each Saturday. The last line of the calendar must end with a new line, so the loop should be followed by the followed statement:
if (weekday != Sunday) printf(“\n”);
which ensures that a newline character follows the last line even if that week did not complete the cycle back to Sunday.
At this point, only three tasks remain:
1. Figuring out the number of days in the month
2. Determining on what day of the week the beginning of the month falls
3. Indenting the first line of the calendar so that the first day appears in the correct position
The strategy of stepwise refinement suggests that you should not try to solve these problems at this level of the decomposition. Instead, you can turn these three operations into calls to functions that you implement later. Applying this strategy enables you to write a complete implementation of PrintCalendarmonth:
void PrintCalendarMonth (int month, int year) {
int weekday, nDays, day;
Using stepwise refinement to implement PrintCalendarMonth means that writing the implementations for three of the functions used within it—MonthDays, FirstDayOfMonth, and IndentFirstLine—was deferred until later. When you go back to fill in these missing pieces, you can implement the functions in any order, but you must complete them all before you can execute the program.
The easiest function to implement is the last one in the list: IndentFirstLine. This function is intended to take the day of the week supplied by FirstDayOfMonth and make sure that the first line of the calendar starts with enough blank spaces so that the first day appears at the correct position. If the month begins on a Sunday, the calendar should start immediately at the beginning of the first line. If it begins on a Monday, the program needs to print out one data’s worth of spaces to account for the missing Sunday. Because each calendar entry
The verse lists several independent case, which suggest the use of a switch statement. The complete implementation of the function follows:
} }
You already have an implementation of IsLwapYear from the section on ―Predicate functions‖ earlier in this chapter, and you should simply use it. It is almost always better to use existing code than to rewrite it from scratch.
The FirstDayOfMonth function could be hard to implement, but only if you try to clever.
A simple, workable strategy is to pick some day in history and count forward from there.
Computers are, after all, quite fast; the user won’t notice any delay. For example, January 1, 1900, fell on a Monday. For every year since then, you need to add 365 or 366 days, depending on whether the year was a leap year. For each month of the current year preceding the one in question, you need to add the number of days in that month. By performing these calculations using modular arithmetic and taking the remainder after dividing by 7, the program can compute the weekday for the beginning of any month since 1900 using the following implementation:
int FitstDayOfMonth (int month, int year) {
int weekday, i;
weekday = Monday;
for (i =1900; i < year; i++) [
weekday = (weekday + 365) %7;
if (IsLeapYear (i)) weekday = (weekday + 1) % 7;
}
for (i = 1; i < month; i++) {
weekday = (weekday + MonthDays(i, year)) % 7;
}
return (weekday);
}