flag標誌什麼?哦,它標誌程式碼餿了——(三)

weixin_34054866發表於2011-11-19

  有時候flag會以別的名字出現,但同樣會把程式碼弄得餿不可聞。例如下面的程式碼

樣本程式碼5

View Code
#include <stdio.h>
#include <string.h>
#define N 10
int main( void )
{
/**/
int num[N],number;
char name[N][8];
/ * */
printf("\ninput number to look for:");
scanf("%d",&number);
search(number,num,name);
/**/
return 0;
}

void search(int n,int num[],char name[N][8])
{int top,bott,mid,loca,sign;
top=0;
bott=N-1;
loca=0;
sign=1;
if((n<num[0])||(n>num[N-1]))
loca=-1;
while((sign==1)&&(top<=bott))
{mid=(bott+top)/2;
if(n==num[mid])
{loca=mid;
printf("No.%d,his name is %s.\n",n,name[loca]);
sign=-1;
}
else if(n<num[mid])
bott=mid-1;
else
top=mid+1;
}
if(sign==1||loca==-1)
printf("%d not been found.\n",n);
}

    ——譚浩強 ,《C程式設計(第四版)學習輔導》,清華大學出版社,2010年7月,p92

  這段程式碼中的search()函式的主要功能是用二分法在已排好序的num陣列中搜尋n,求出n在num陣列中的位置loca後輸出對應在name陣列中相同下標的元素name[loca]。

  這個search()函式的第一個毛病是缺少陣列尺寸引數,char name[N][8]這個引數中的N毫無意義,函式內的N則來得無緣無故。這是一個很大的毛病,這使得函式喪失通用性,而沒有通用性的函式基本上就是是廢品函式。但這些由於和flag無關,所以這裡暫且放置不談。

  在這個函式內,loca和sign都屬於那種flag變數,儘管沒有取名為flag。

  loca這個變數用來記錄n在num陣列中的位置,如果陣列中不存在則將loca的值置為-1。將loca的初值取為0極不合理,因為這表示一開始就假設n在陣列中存在且為第一個元素,這是毫無道理的假設。loca合理的初值應該為-1。這樣,只有在找到的情況下這個值才會改變,程式碼可以大為簡化:

View Code
void search(int n,int num[],char name[N][8])
{
int top = 0 ,bott = N-1,mid,loca=-1,sign = 1 ;
while((sign==1)&&(top<=bott))
{
mid=(bott+top)/2;
if(n==num[mid])
{
loca=mid;
printf("No.%d,his name is %s.\n",n,name[loca]);
sign=-1;
}
else if(n<num[mid])
bott=mid-1;
else
top=mid+1;
}
if(loca==-1)
printf("%d not been found.\n",n);
}

  現在不難看出,程式碼中的sign不過是break或return語句的一個拙劣的替代品而已,完全沒有必要。程式碼可以進一步簡化

void search( int n , int num[] , char name[][8] )
{
   int top=0,bott=N-1,mid;

   while( top<=bott )
   {
      mid=(bott+top)/2;
      if( n == num[mid] )
      {
          printf("No.%d,his name is %s.\n",n,name[mid]);
          return  ;
      }
      else if( n < num[mid] )
         bott = mid - 1 ;
      else
         top  = mid + 1 ;
  }

  printf("%d not been found.\n",n);

}

  結論就是,原來程式碼中的loca和sign沒有任何存在的意義,因為它們除了把程式碼弄得更晦澀更復雜之外沒有起到任何好作用。

  標誌變數的毛病改完了,但這個search()其實還有另外的毛病,這個毛病就是根本不應該把查詢與輸出這兩個功能攪和在一起。完成單一任務是函式設計的一個基本原則。因此從整個程式設計的角度來說更乾淨的寫法應該是:  

#include <stdio.h>

#define N 10
#define NOT_FOUND (-1) 

int search( int  , int [] , size_t );

int main( void )
{
   /*  */
   int num[N] , number ;
   char name[N][8] ;
   / *  */
   printf("\ninput number to look for:");
   scanf("%d",&number);
   {
      int site = search( number , num , N );

      if( site != NOT_FOUND )
         printf("No.%d,his name is %s.\n" , number , name[site] );   
      else
         printf("%d not been found.\n",n);
   
   }
   /* */
   return 0;
}

int search( int  n, int num[] , size_t size )
{
   int top  = 0         ,
       bott = size - 1  , 
       mid  ;

   while( top <= bott )
   {
      mid = ( bott + top ) / 2 ;
      if( n == num[mid] )
          return  mid ;
          
      if( n < num[mid] )
          bott = mid - 1 ;
      else
          top  = mid + 1 ;
   }

   return  NOT_FOUND ; 
}

 

  通過前面的幾個例子,不難看出,在程式碼中輕率地使用flag之類的標誌變數確實能夠敗壞程式碼的味道,使得程式碼變餿。

  那麼,是否在程式碼中絕對不應該使用標誌變數呢?卻也不是。使用flag標誌變數的前提首先是解決問題的演算法要求使用這樣的變數,離開了這個前提使用flag標誌變數就難免成為東施效顰;其次,不應該生硬死板地為標誌變數命名為flag,無論什麼問題總是一味地flag或sign,不是頭腦僵化就是思維扭曲。

  最後看一個例子,欣賞一下大師是如何使用標誌變數的。下面的程式碼出自K&R,程式功能是統計輸入的行數、單詞數和字元數:

#include <stdio.h>

#define IN  1     /*在單詞內*/
#define OUT 0     /*在單詞外*/
/* 統計輸入的行數、單詞數和字元數 */
main()
{
  int c,nl,nw,nc,state;
  
  state = OUT ;
  nl = nw = nc = 0 ;
  while((c = getchar())!= EOF){
      ++nc;
      if( c == '\n')
         ++nl;
      if( c ==' ' || c =='\n' || c =='\t')
         state = OUT ;
      else if(state == OUT){
         state = IN ;
         ++nw;
      }      
  }
  printf("%d %d %d\n", nl , nw , nc );
}

   這裡,標誌變數並沒有使用醜陋的flag作為名稱,而是恰當地使用了state這個與問題相貼切的名字。再加上兩個漂亮的符號常量IN和OUT,使得程式碼的含義不言自明,連註釋都用不著。三十多年過去了,這段程式碼不但沒有腐朽,相反,依然還是那麼清新典雅,垂範後人,令人高山仰止。(完)

相關文章