摘要:統(tǒng)計單詞個數(shù)的程序是一個經(jīng)典的C語言程序,傳統(tǒng)的寫法存在結(jié)構(gòu)復雜、可讀性差的不足。通過對相鄰的兩個字符同時進行判斷,即可簡化程序結(jié)構(gòu),改善程序的可讀性。
關鍵詞:單詞個數(shù);標記變量;結(jié)構(gòu)性;可讀性
中圖分類號:TP312? ? ? ? 文獻標識碼:A
文章編號:1009-3044(2020)36-0071-02
培養(yǎng)學生良好的編程能力是程序設計課程的核心目標[1],而編程能力的培養(yǎng)離不開一個個具體的案例程序。
在教學中選用優(yōu)質(zhì)的案例程序,對于培養(yǎng)學生的程序設計能力具有重要的示范引導作用[2-3],因此應該選擇更加合理有效的程序?qū)崿F(xiàn)方法。不過,在當前的C語言程序設計教材中,仍有個別案例程序的寫法存在改進優(yōu)化的空間。
1 統(tǒng)計單詞個數(shù)的傳統(tǒng)程序
“統(tǒng)計一個字符串中單詞的個數(shù)(假定單詞之間以空格分隔)”是C語言程序設計中的一個經(jīng)典程序,是字符串處理部分的一個重要案例[4-5]。
下面的程序是在許多C語言教材中采用的一種傳統(tǒng)寫法。
源程序1:
#include
#define IN 1? ? ? /*表示在一個單詞內(nèi)部*/
#define OUT 0? ? ? /*表示在一個單詞外部*/
int main(void)
{
char c;
int num,state;
state=OUT;? ? ? /*初始狀態(tài)位于單詞外部*/
num=0;
while((c=getchar())!='\n')
{
if(c==' ')
state=OUT;
else if(state==OUT)
{
state=IN;
num++;
}
}
printf("單詞個數(shù)=%d\n",num);
return 0;
}
這是一個經(jīng)典的程序,同時也是一個比較晦澀難懂的程序。由于第一個單詞之前可能有空格,同時兩個單詞之間也可能有多個空格,因此不能簡單地通過統(tǒng)計空格的個數(shù)以得到單詞的個數(shù)。該程序的編程思路是依據(jù)相鄰的兩個字符進行判斷,當相鄰的兩個字符中,前一個是空格符(即處于單詞外部),而后一個是非空格字符(即處于單詞內(nèi)部)時,說明找到一個新的單詞的開頭,從而將單詞的個數(shù)加一。
由于在每次循環(huán)中只對當前讀入的一個字符進行判斷,因此在讀入下一個字符之前,必須將當前字符對應的狀態(tài)記錄到標記變量state中。若當前字符是空格符(即處于單詞外部),則state取值為OUT;若當前字符是非空格字符(即處于單詞內(nèi)部),則state取值為IN。當然,在讀入任何字符之前,state也應該取值為OUT,即處于單詞外部。
從而在讀入新字符時,若state的值為OUT,則說明前一個字符是空格符;若state的值為IN,則說明前一個字符是非空格字符。因此,當讀入的新字符是非空格字符,而state的值為OUT(即前一個字符是空格符)時,說明找到一個新的單詞的開頭。
這種寫法之所以結(jié)構(gòu)復雜、可讀性差,不容易為初學者理解和掌握,其主要原因就在于每次循環(huán)只對當前一個字符進行判斷,而不是對相鄰的兩個字符同時進行判斷。
2 改進的程序
如何改進這個程序呢?其實,只需要利用兩個字符變量存儲相鄰的兩個字符,并在循環(huán)體中同時對相鄰的兩個字符的值進行判斷,就可以取消標志變量state,從而降低程序的復雜度。
為了便于編寫程序,可以設想在整個字符串之前添加一個空格,這并不影響單詞個數(shù)的統(tǒng)計結(jié)果。
下面是改進之后的C語言源程序。
源程序2:
#include
int main(void)
{char c0,c;
int num;
num=0;
c0=' ';? ?/*設想在整個字符串之前添加一個空格*/
while((c=getchar())!='\n')? ?/*輸入一個字符并存入變量c中*/
{
if(c0==' ' && c!=' ')? /*相鄰的兩個字符中,前一個是空格符,后一個是非空格字符*/
num++;? ?/*說明找到一個新的單詞的開頭,將單詞的個數(shù)加一*/
c0=c;? ? /*將變量c的值轉(zhuǎn)存到變量c0中,為輸入下一個字符做好準備*/
}
printf("單詞個數(shù)=%d\n",num);
return 0;
}
程序運行結(jié)果:
在該程序中,利用兩個字符變量c0和c存儲相鄰的兩個字符,其中c0存儲前一個字符,c存儲后一個字符。這樣,只需要同時對c0和c的值進行判斷,即可確定是否找到一個新的單詞。也就是當c0的值是空格符,而c的值是非空格字符時,說明找到一個新的單詞的開頭,從而將單詞的個數(shù)加一。
可以發(fā)現(xiàn),經(jīng)過改進之后,程序在結(jié)構(gòu)性和可讀性方面均有較大程度的優(yōu)化,從而降低了程序的復雜度,提高了學習者的接受度。
3 利用字符數(shù)組實現(xiàn)的程序
在前面的程序中,利用getchar函數(shù)逐個輸入字符,利用兩個字符變量的值不斷交替,存儲相鄰的兩個字符。
還有一種處理方式,就是利用一個字符型數(shù)組存儲字符串。由于在第一個單詞之前有可能沒有空格符,因此若仍然采用查找一個單詞的開頭的方式,實現(xiàn)起來將會不甚方便。不過,可以變換一下思路,改為查找一個單詞的末尾。
可以發(fā)現(xiàn),在除了最后一個單詞之外的每個單詞之后至少有一個空格符,而在最后一個單詞之后可能跟一個空格符,也可能直接跟一個空字符'\0',因此可以將判斷規(guī)則修改為“當相鄰的兩個字符中,前一個是非空格字符,而后一個是空格符或空字符'\0'時,說明找到一個新的單詞”[6]。
下面是利用字符數(shù)組實現(xiàn)的C語言源程序。
源程序3:
#include
#include
int main(void)
{char a[200];
int i,n,c=0;
printf("請輸入一行以空格分隔的單詞:\n");
gets(a);
n=strlen(a);
for(i=0;i<=n-1;i++)
{if(a[i]!=' '&&(a[i+1]==' '||a[i+1]=='\0'))
c++;
/*若第i個字符不是空格符,第i+1個字符是空格符或'\0',則表示找到一個單詞的末尾*/
}
printf("單詞個數(shù)=%d\n",c);
return 0;
}
可以發(fā)現(xiàn),相對于前面的程序,利用字符型數(shù)組統(tǒng)計單詞個數(shù)的程序在結(jié)構(gòu)性和可讀性方面均有進一步的改進。
4 進一步改進的程序
在前面的程序中均假定單詞之間是以空格符分隔的,而在現(xiàn)實中單詞之間也可以用標點符號分隔,如何應對這個問題呢?其實,只需要對源程序3稍加修改,就可以適應這種新的要求。
由于單詞之間是以空格符或標點符號分隔的,而標點符號是不便于一一判斷區(qū)分出來的,因此可以通過判斷一個字符是不是字母或數(shù)字來確定是否處于單詞內(nèi)部。也就是當相鄰的兩個字符中,前一個是字母或數(shù)字(即處于單詞內(nèi)部)而后一個不是字母或數(shù)字(即處于單詞外部)時,說明找到一個新的單詞的末尾[6]。要判斷一個字符是不是字母或數(shù)字,可以直接調(diào)用C語言中的isalnum庫函數(shù)。
從而可以寫出如下進一步改進的源程序。
源程序4:
#include
#include
#include
int main(void)
{char a[200];
int i,n,c=0;
printf("請輸入一行以空格或標點分隔的單詞:\n");
gets(a);
n=strlen(a);
for(i=0;i<=n-1;i++)
{if(isalnum(a[i])&&!isalnum(a[i+1]))
c++;
/*若第i個字符是字母或數(shù)字,第i+1個字符不是字母或數(shù)字,則表示一個單詞結(jié)束*/
}
printf("單詞個數(shù)=%d\n",c);
return 0;
}
程序運行結(jié)果:
5 結(jié)論
通過以上程序的改進過程可以發(fā)現(xiàn),只要不迷信于教材中的經(jīng)典案例,勇于改進,勇于創(chuàng)新,就能夠向?qū)W生傳授更加科學合理的知識和技能。而這個案例的改進過程本身,也是對學生進行創(chuàng)新教育的一個很好的樣板。
參考文獻:
[1] 陳濤. 面向編程能力培養(yǎng)的C語言教學模式研究[J].計算機教育,2020(1):100-103.
[2] 薛小鋒.案例教學在非計算機專業(yè)“C語言程序設計”教學中的應用[J].江蘇技術師范學院學報,2010(4):80-82,88.
[3] 丁海燕.高級語言程序設計案例教學模式的探討[J].計算機教育,2011(8):65-68.
[4] 譚浩強.C程序設計[M].5版.北京:清華大學出版社,2017.
[5] 田淑清.全國計算機等級考試二級教程——C語言程序設計(2016年版)[M].北京:高等教育出版社,2015.
[6] 巨同升.C語言程序設計新思路[M].北京:科學出版社,2020.
【通聯(lián)編輯:王力】