C++之初识“类” (3):自己写的MyDate类的一些改进

前言

虽然这几天很忙,写文档也很花时间,但是还是控制不住想写的心情(Markdown语法好用)。我想介绍另一种更快速的推演日期的方法,同时改进一下日期间隔计算,再把我上一期留下的坑填了……

#往期传送门#
C++之初识“类”:写一个关于日期的类(附两种日期间隔计算)
https://blog.csdn.net/henry_23/article/details/104832525
C++之初识“类” (2):增添“日期”MyDate类的功能
https://blog.csdn.net/henry_23/article/details/104906562

一些改进

日期间隔计算:代码 / 算法上的一些改进

还是先来说说日期间隔计算,在和同学交流过后,面向过程的算法有了许多可以改进的地方(这里要感谢我的一个室友,每次看他写的代码都会有新收获!)

其实,也不能算是改进吧,大概就是可以少写一些代码的样子;没有仔细研究,所以效率有没有变高不好说。想看最初版本的读者请移步传送门第一篇文章。

首先,同年的时候免去是否同月的讨论,只需要分别计算两个日期到1月1日的天数,然后两者相减即可,如下:

if (ey == cy)
{
    
    
    int sum1 = 0, sum2 = 0;
    for (int i=1; i<cm; i++) sum1 += days(cy,i);
    sum1 += cd;
    for (int i=1; i<em; i++) sum2 += days(cy,i);
    sum2 += ed;
    sum = sum2 - sum1;
}

还是在室友的启发下,我发现,也不必判断日期谁先谁后了,只需要判断年份是否相等即可,因为,如果同年,则通过绝对值法可以解决问题;如果跨年,则根据情况用365或者366减去已经求得的日期,即可实现从“一年的剩余天数”到“一年的已过天数”的转换。代码如下:

int MyDate::gap1(const MyDate& date)
{
    
    
    int sum = 0;
	if (year != date.year)
	{
    
    
		//分别计算当前年到年末的天数,和目标年已经走过的天数
		sum += (days(year,month)-day);
		for(int i = month+1; i <= 12; i++) sum += days(year,i);
        sum += date.day;
		for(int i = 1; i < date.month; i++) sum += days(date.year,i);

		int y1 = year, y2 = date.year;
		if (y1 > y2) //如果年份y1>y2,说明目标日期早于当前日期
		{
    
    
			//因此求得的日期需要“转换”
			int t = 365 * 2 + isLeap(y1) + isLeap(y2);
			sum = t - sum;
			swap(y1,y2);
		}
		for(int i = y1+1; i<y2; i++)
		{
    
    
			if(isLeap(i)==1) sum += 366;
			else sum += 365;
		}
	}
	if(year == date.year)
	{
    
    
		int sum1=0,sum2=0;
		for(int i=1; i<month; i++) sum1+=days(year,i);
		sum1 += day;
		for(int i=1; i<date.month; i++) sum2+=days(date.year,i);
		sum2 += date.day;
		sum = abs(sum2-sum1);
	}
	return sum;
}

紧接着又想到,利用“减法取反”的思想,代码量还可以减少一些,即分别计算两个日期到当年1月1日的天数,然后对于同年,直接相减取绝对值;对于跨年,根据情况,用365或366减去其中那个较早年份计算得到的结果,即可转化成该日期到年末的天数,再加上中间间隔年份的天数即可完成计算。

int MyDate::gap1(const MyDate& date)
{
    
    
    int sum = 0, sum1 = 0, sum2 = 0;
	for(int i=1; i<month; i++) sum1 += days(year,i);
	sum1 += day;
	for(int i=1; i<date.month; i++) sum2 += days(date.year,i);
	sum2 += date.day;
    
    if (year == date.year) sum = abs(sum2-sum1);
    else
	{
    
    
		int y1 = year, y2 = date.year;
		if (y1 > y2) 
        {
    
    
            swap(y1,y2); 
            swap(sum1,sum2);
        }
        sum = sum2 + (365 + isLeap(y1) - sum1);

		for (int i = y1+1; i<y2; i++)
		{
    
    
			if (isLeap(i)==1) sum+=366;
			else sum+=365;
		}
	}
	return sum;
}

日期推演算法的改进

因为本人比较懒,也不聪明,所以上期该功能的实现是使用while循环多次执行tomorrow()或者yesterday()实现的,效率上很低下。也是在看了室友的代码过后才发现,其实同样使用while循环,同样是“进位”的思想,就能够实现高效的日期推演。(我在上期说写起来会很复杂,这里打脸了…)

下面放上同学的代码(我对其做了一些变量名的修改)。说明一下,days()函数,可以返回当前月份的天数,详细情况请查看前篇博客。

扫描二维码关注公众号,回复: 12493948 查看本文章

forward:日期向后推演

跟着代码走一遍,很容易就能明白,我就不解释了。

void MyDate::forward(int k)
{
    
    
    int sum_days = k + day;
    day += k;
	while(day > days())
	{
    
    
		day -= days();
    	month++;
    	if(month > 12)
		{
    
    
			year++;
			month = 1;
		}
	}
}

rollback:日期向前推演

这个比forward()稍微难想那么一点点,毕竟“退位”涉及到“退位”操作,按理说比“进位”稍微难一点。

void MyDate::rollback(int k)
{
    
    
    while(k >= day)
	{
    
    
		k = k - day;
		month--;
		if(month<1)
		{
    
    
			year--;
			month = 12;
		}
		day = days();
	}
	day -= k;
}

rolling 函数,调用上面两者

单独执行上面两个函数,就可以完成日期的向前、向后推演。我套用上期自己写的rolling()函数的模板,将两个功能整合到了一起,方便使用,同时可以避免直接更改源数据、并且直接输出推演的结果。

至于函数参数的解释,也请参见上一篇博……嗯,我还是简单说一下吧:

rolling()有两个参数,一个是整型的k,用来传入需要前进 / 回滚的天数;另一个是默认值为0的整型参数mode,这是由于老师的要求“显示指定天数之前/之后的日期”,没有说明是否需要改变原函数,所以设置了它,用来限定是否修改元数据(默认不修改);

void MyDate::rolling1(int k, int mode = 0)
{
    
    
    if (mode == 0) {
    
    
        MyDate tmp(year,month,day);
        print();
        cout<<" 之";
        if (k>0) {
    
     tmp.forward(k); cout<<"后 "; }
        else {
    
     tmp.rollback(-k); cout<<"前 "; }
        cout<<abs(k)<<" 天是 ";
        tmp.print();
        cout<<endl;
    }
    else {
    
    
        if (k>0) forward(k); 
        else rollback(-k);
    }
}

新功能(填上期的坑)

多风格输出

定义了一个静态成员变量style,用来指定风格,同时拓展了print()函数的功能。

似乎这里可以在set_style()里面用枚举型?奈何本人学艺不精,不会使用,有兴趣者可以试试。

void MyDate::set_style(int t) {
    
    style = t;};
void MyDate::print()
{
    
    
    if (style == 0) cout<<year<<"年"<<month<<"月"<<day<<"日";
    else if (style == 1) cout<<year<<"/"<<month<<"/"<<day;
    else if (style >1 && style < 4) //2 Amreican 3 English
    {
    
    
        char months_eng[13][15] = {
    
    "0","January","February","March","April",
            "May","June","July","August","September","October","November","December"};
        if (style == 2) cout<<months_eng[month]<<" "<<day<<", "<<year;
        else if (style == 3) cout<<day<<" "<<months_eng[month]<<", "<<year;
    }
}

样例展示
style = 12020/3/12
style = 2March 12, 2020
style = 312 March, 2020

当然,也可以加上输出序数词日期的功能:先判断日期是否落在11~13日,如果是,输出th;如果不是,则用day % 10求得日期个位,相应地输出输出stndrd或者th

	char s[4][4]={
    
    "th","st","nd","rd"};
	if (style == 4) 
    {
    
    
		cout<<months_eng[month]<<" ";
		cout<<day;
		if(day>10&&day<14) cout<<s[0];
		else 
		{
    
    
			if (day%10 > 0 && day % 10 < 4) cout<<s[day%10];
			else cout<<s[0];
		}
    	cout<<", "<<year;
	}

也可以把英文月份换作缩写:

char months_eng_short[13][8] = {
    
    "0","Jan.","Feb.","Mar.",
	"Apr.","May","June","July","Aug.","Sept.","Oct.","Nov.","Dec."};

还可以加入对其他语言的支持,或者把输出该日是星期几的功能也整合进去(相应功能见下边)。

今天星期几?

“Which day is it today?” 今天是星期几呢?让我们来写一个函数实现它吧!

先说明一下,这些代码都没有参考网上的代码实现,均是我或者我同学想出来的(当然我同学有没有上网上看就不知道了),有些思想是之前在网上看到过而已,比如求今天是星期几。

如前文所说,根据现有的函数,可以设定一个基准日期,计算与之的日期间隔,然后结果对7求余,同时判断日期是在基准日之前还是之后,继而得出该日期是星期几。

我选了最近的一个星期日,2020年3月15日;当然你也可以选择任何一个日期作为基准日,选星期日可能会方便一点。

get_day()函数会返回一个1-7的整数,用来表示今天是星期几;比如返回值是1,就表示星期1,返回值是5就表示星期五。

int MyDate::get_day()
{
    
    
    MyDate date(2020,3,15); //Sunday;
    int dif = gap(date);
    dif %= 7;
    if (dif == 0) return 7;
    //current date is later than the standard date:
    else if (compare(date)==1) return dif;
    else return 7-dif;
}

还要写一个函数,用来输出“用户友好”的内容。这个函数会去访问style这个静态成员变量,输出中文或者英文的“星期几”。

void MyDate::print_day()
{
    
    
    if (style == 0 || style == 1)
    {
    
    
        cout<<"星期";
        char str[8][4] = {
    
    "一","二","三","四","五","六","日"};
        cout<<str[get_day()-1];
    }
    else 
    {
    
    
        char str[7][12]={
    
    "Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"};
        cout<<str[get_day()-1];
    }
}

结语

至此应该MyDate类的功能应该是很完善了,一时想不到什么其他功能了,如果有想法欢迎留言交流。我个人感觉能继续做的方面,可以是更加完善风格输出,或者定义一些运算符重载(八成我是不会做了,除非老师布置作业哈)。

放一下现在的MyDate类声明,其中像gap1()rolling1()这种是集成进来的别的同学的功能实现(forward()rollback()也是),最终的运行结果都是一样的。

class MyDate 
{
    
    
    private:
        int  year, month, day;
        static int style;
    public:
        static void set_style(int t);
        void set(int y_, int m_, int d_);
        void set(const MyDate& );
        int  days();
        int  days(int, int);
        void print();
        int  equals(const MyDate& );
        void tomorrow();
        int  isLeap();
        int  isLeap(int );
        int  isLegal();
        int  compare(const MyDate& );
        int  get_gap(const MyDate& );
        int  gap(const MyDate& );
        int  gap1(const MyDate& );
        void forward(int);
        void rollback(int);
        void rolling(int, int );
        void rolling1(int, int );
        int  get_day();
        void print_day();
        MyDate yesterday();

    MyDate();
    MyDate(int y_, int m_, int d_);
};
int MyDate::style = 0;

最后再次感谢和我交流的同学,真的收获良多!

猜你喜欢

转载自blog.csdn.net/henry_23/article/details/104958317
今日推荐