1. 程式人生 > >用陣列代替if-else和switch-case語句

用陣列代替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<int, int>來將原資料轉為一堆連續的數字,這樣就又可以用到表驅動法了。另外,也可以使用其他更加高階的資料結構來實現,不需要侷限於陣列,關鍵都是使用空間來換取時間。