Osheep

时光不回头,当下最重要。

天花板编程手把手计划-第1期-第8天

《天花板编程手把手计划-第1期-第8天》

上一篇中的课后练习大家普遍反映太简单了。其实,麻雀虽小,五脏俱全。今天我们来看看它的解法。

1. 题目

请编程实现一个功能,输入任意一个日期,计算出那一天是星期几。这道题没有任何限制,你可以设计任何自己喜欢的交互形式,可以使用任何自己能想到的算法。

2. 分析

这道题的题干并不是很具体,它其实是一道从功能开始的设计题。首先要考虑的是交互方法的设计。程序的输入时一个日期,这个日期采用什么形式,怎么输入都是不确定的。

为了Debug方便,我依然用了之前的程序中习惯的方法,从文件读取测试用例。于是,这个题目变成了这样。

给出一个文件input.txt,内容如下:

5
1900-01-01 1
2017-5-24 3
2017-1-1 0
2017-3-6 1
2017-4-20 4

第一行给出的是日期的个数,之后每一行给出两个元素:日期和星期。请编程实现判断日期是星期几,之后用后面的正确答案来判断程序是否执行正确。

这个题目看起来比较奇怪,居然给出了正确答案。其实这是软件开发中常用的一种方法叫做单元测试。我们可以通过这种方法来测试程序实现的功能是否正确。

3. 程序主干

我们首先来实现一个main函数,代码如下:

int main()
{
    int i, cnt;

    freopen("input.txt", "r", stdin);
    scanf("%d", &cnt);

    for (i = 0; i < cnt; i++)
    {
        printf("#%d : ", i + 1);
        if (Process() == 1)
        {
            printf("Right\n");
        }
        else
        {
            printf("Wrong\n");
        }
    }

    return 1;
}

是不是很熟悉,这段程序首先从文件中读取测试用例的个数,保存在cnt变量中。之后循环来判断核心的算法是否正确,正确在屏幕上打印Right,错误打印Wrong。

判断测试核心算法的函数是Process,接下来我们就实现它。

4. 测试函数

Process函数源码如下:

#define MAX 15
char g_arr[MAX];
int Process()
{
    int ret, answer, i;
    int year, month, day;
    int week;
    char* pStr = g_arr;

    scanf("%s", pStr);
    scanf("%d", &answer);

    year = GetNextNum(&pStr);
    month = GetNextNum(&pStr);
    day = GetNextNum(&pStr);

    week = GetWeek(year, month, day);

    if (week == answer)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

这个函数有三部分功能:

4.1 读取数据

这部分很简单,通过scanf从文件中读取数据,这个前面的文章中已经讲过了。这里需要说明的是我们拿到一个类似“2017-01-01”这样的字符串,如何得到它对应的年月日对应的int值呢。这里调用了一个GetNextNum函数,它的实现如下:

// 得到字符串中的第一组数字
int GetNextNum(char** ppStr)
{
    int ret = 0;
    int num;
    char* pStr = *ppStr;
    while (1)
    {
        if (*pStr < '0' || *pStr > '9')
        {
            break;
        }

        num = *pStr - '0';
        ret = ret * 10 + num;

        pStr++;
    }
    pStr++;
    *ppStr = pStr;

    return ret;
}

这个方法应该是教科书的练习题,相信大家都能看懂。需要说明的是,参数使用了char** ppStr这个二维指针,原因在于我们希望每次调用这个函数时,都能够从上一次处理之后的位置开始。如果写成char* pStr,会出现什么样的情况,请大家自己试验。

4.2 判断星期

判断星期时,调用的就是我们要测试的核心算法函数GetWeek,这个函数的功能是输入年月日三个参数,返回一个星期数,0~6表示星期日到星期六。

4.3 判断是否正确

GetWeek函数的结果与文件中的正确结果比较,正确返回1,错误返回0。

5. 判断星期

之前的文章21天C语言代码训练营(第六天)里曾经定义过相关的接口,我们把需要的几个拿过来。

// 判断闰年,是闰年返回1,是平年返回0
int IsLeapYear(int year)
{
    if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
        return 1;
    else
        return 0;
}

// 返回输入年份的1月1日是周几
int GetFirstWeek(int year)
{
    return (35 + year + year / 4 - year / 100 + year / 400) % 7;
}

int g_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

// 返回输入的年份中输入的月份天数
int GetDays(int year, int month)
{
    if (month == 2 && IsLeapYear(year))
    {
        return g_days[month - 1] + 1;
    }
    else
    {
        return g_days[month - 1];
    }
}

这三个函数的功能都很简单,相信一看就能明白。之前有很多人问GetFirstWeek这个函数为什么这样实现,这个是网上找的算法,具体的推导过程我没有留意,只是验证了一下发现能用而已。

有了这几个接口,我们就能很轻松地写出今天的核心函数了:

int GetWeek(int year, int month, int day)
{
    int i;
    int week = GetFirstWeek(year);
    for (i = 1; i < month; i++)
    {
        week += GetDays(year, i);
    }

    week--;

    week += day;
    week = week % 7;

    return week;
}

这个函数的功能是输入年月日三个参数,返回当天是星期几。

好了,现在运行一下程序,看看执行结果。

《天花板编程手把手计划-第1期-第8天》

这个题目虽然简单,但这种通过一段程序测试另一段程序的思想希望大家仔细体会。

6. 课后练习

《天花板编程手把手计划-第1期-第8天》

用你学过的C语言知识,设计一个番茄钟程序。要求满足如下功能:

  • 可以设置倒计时时间
  • 拥有倒计时效果
  • 倒计时结束后有提醒
  • 使用记录保存在文件中

我是天花板,让我们一起在软件开发中自我迭代。
如有任何问题,欢迎与我联系。


上一篇:天花板编程手把手计划-第1期-第7天

点赞