4.3.2 第二层设计
接下来需要对第二层上的每个模块进行精化。
首先看 getYear 函数。这个函数的功能只是输入年份数据,可以直接用 Python 的基本 语句实现,无需分解为新的功能模块。具体代码如下:
def getYear():
print "This program prints the calendar of a given year."
year = input("Please enter the year (after 1900): ")
return year
接着考虑 firstDay 函数的设计。这个函数的功能是计算 year 年 1 月 1 日是星期几,因为年历是按星期来组织每一天的显示位置的,而只要知道 1 月 1 日的显示位置,其后所有日期的显示位置也就确定了。
在 calendar 程序的规格说明中说明了,我们以 1900 年 1 月 1 日(星期一)作为基准日, 只要算出 year 年 1 月 1 日距离基准日的天数,就能知道这一天是星期几。因为从基准日开 始,过 1 天是星期二,过 2 天是星期三,…,过 6 天是星期日,过 7 天又是星期一,…。一 般地,过 n 天是星期(n+1)%7(值为 0 表示星期天)。
那么,从基准日到 year 年 1 月 1 日总共过了多少天呢?只需一点常识,就能得出下面 的公式:
(year – 1900) * 365 + k
其中 k 是从 1900 到 year(不含)之间的闰年个数。
看上去闰年个数 k 还不清楚如何求得,我们按惯例假设一个新函数 leapyears()能够
返回所需的 k。于是可以设计 firstDay 函数如下:
def firstDay(year):
k = leapyears(year)
n = (year – 1900) * 365 + k
return (n + 1) % 7
最后考虑 printCalendar 函数的设计,该函数的任务是在合适的位置按日历格式显示一年 12 个月的日历。由于问题有点复杂,我们照例进行任务分解。12 个月的日历输出显然可以用一个 for 循环来实现,循环体是显示一个月日历的代码。每个月需要先打印标题(月份和星期的名称),然后再打印日期,假设函数 heading()和 oneMonth()分别执行这两个任务,则 printCalendar 的代码如下:
def printCalendar(year,w):
print "=========== " + str(year) + " =========="
first = w
for month in range(12):
heading(month)
first = oneMonth(year,month,first)
函数体的第一行用于打印年份信息,接下去是打印 12 个月的日历的 for 语句。打印每个月 的日历需要知道该月 1 日是星期几。printCalendar 的参数 w 是前面算出来的 1 月 1 日 的星期信息,2 月到 12 月的 1 日则由 oneMonth 函数返回至此,我们完成了第二层设计, 可以用图 4.8 中的结构图表示到目前为止的设计结果。注意,为简明起见,图中省略了各模 块之间的界面数据。
图 4.8 calendar 程序的第二层结构图