用数组代替if-else和switch-case语句

       表驱动法(Table-Driven Approach),通过在表中查找信息,来代替很多复杂的if-else或者switch-case逻辑判断。这是一种设计的技巧,可以应用很多的场合,不仅可以提高程序的性能,也能大大减少代码量,使得代码变得高效和优雅。下面将使用一个例子来展示这种方法的优点。

       当我们需要写一个函数用来获取指定月份的天数时,我们可能会写出以下的代码段:

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

//获取year年month月的天数
unsigned int GetMonthDays(unsigned int year, unsigned int month)
{
	unsigned int days = 0;
	if (1 == month) {days = 31;}
	else if (2 == month)
	{
		if (IsLeapYear(year)) {days = 29;}
		else {days = 28;}
	}
	else if (3 == month) {days = 31;}
	else if (4 == month) {days = 30;} 
	else if (5 == month) {days = 31;} 
	else if (6 == month) {days = 30;} 
	else if (7 == month) {days = 31;} 
	else if (8 == month) {days = 31;} 
	else if (9 == month) {days = 30;} 
	else if (10 == month) {days = 31;} 
	else if (11 == month) {days = 30;} 
	else if (12 == month) {days = 31;} 

	return days;
}

       上面的代码通过多个if-else语句,来获取当前月份的总天数,代码看起来还算清晰,但是就是感觉有点冗长和重复,另外,需要进行多次比较才能得到具体的天数,月份越后,比较次数就越多。

       编译器在实现switch-case的时候,会用到跳表来优化比较的性能,下面将上面代码的if-else语句换为switch-case的实现。

//获取year年month月的天数
unsigned int GetMonthDays(unsigned int year, unsigned int month)
{
	int days = 0;
	switch(month)
	{
		case 1: days = 31; break;
		case 2: 
		{
			if (IsLeapYear(year)) {days = 29;}
			else {days = 28;}
		}break;
		case 3: days = 31; break;
		case 4: days = 30; break;
		case 5: days = 31; break;
		case 6: days = 30; break;
		case 7: days = 31; break;
		case 8: days = 31; break;
		case 9: days = 30; break;
		case 10: days = 31; break;
		case 11: days = 30; break;
		case 12: days = 31; break;
		default: break;
	}

	return days;
}

       对于上面的switch-case实现,先不论效率有多大提升,单纯的看代码,感觉跟if-else没多大区别,代码还是有点冗长和重复,还是不够简洁。若应用于其他场景,随着case分支越来越多,case分支的代码量越来越大的时候,代码就更难维护了。其实可以看到,当分支越来越多的时候,每个case分支的逻辑其实还是一样的,只是其中的数据改变了而已。若在加分支的时候不小心还有可能修改了逻辑的代码,容易导致出错。

       此时,通过表驱动法,可以很容易分离变化的数据和不变化的逻辑,代码如下:

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

static unsigned int monthdays[2][12] = 
{
	{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
	{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
}

//获取year年month月的天数
unsigned int GetMonthDays(unsigned int year, unsigned int month)
{
	return monthdyas[IsYeapYear(year)][month - 1];
}

       这次代码够整洁了,不仅代码量变少了,而且通过数组地址偏移操作就可以快速得到数据,不需要一大段的比较逻辑,更加高效了。但是这种表驱动法也不是所有场合都能直接用到的,如果要判断的数据不是连续的,如1、5、16、222,就需要做另外的转换,譬如用一个map来将原数据转为一堆连续的数字,这样就又可以用到表驱动法了。另外,也可以使用其他更加高级的数据结构来实现,不需要局限于数组,关键都是使用空间来换取时间。




你可能感兴趣的:(C/C++)