焦華
摘要:過程化編程是面向?qū)ο缶幊痰幕A(chǔ),它的規(guī)律和特點(diǎn)適合采用案例教學(xué)、適合進(jìn)行思維訓(xùn)練。通過對(duì)典型案例的剖析,在給出了多種解決方案的過程中,本文的亮點(diǎn)是哲學(xué)視野下的思維拓展和思維創(chuàng)新。作為基礎(chǔ)編程訓(xùn)練,本文對(duì)《C/C++程序設(shè)計(jì)》課程教學(xué)有實(shí)用價(jià)值。
關(guān)鍵詞:過程化編程;案例教學(xué);輾轉(zhuǎn)相除法;創(chuàng)新思路
過程化程序設(shè)計(jì)方法即結(jié)構(gòu)化程序設(shè)計(jì)方法是“自頂向下、逐步細(xì)化、模塊化。”,也就是說,過程化程序的基本組成單元是函數(shù)(模塊),一個(gè)程序是由若干個(gè)函數(shù)組成的;而面向?qū)ο蟪绦虻幕窘M成單元是類(class) ,一個(gè)程序是由若干個(gè)類組成的。類里面包含有成員函數(shù),因此過程化編程是面向?qū)ο缶幊痰幕A(chǔ)。過程化編程要求編程人員一步一步地安排好程序的執(zhí)行過程,根據(jù)著名的Wirth公式“算法+數(shù)據(jù)結(jié)構(gòu) =程序”,[1]在過程化程序設(shè)計(jì)中,數(shù)據(jù)結(jié)構(gòu)就像物質(zhì)、算法就像意識(shí)。如同人類的身體和靈魂!哲學(xué)告訴我們:意識(shí)是依賴于物質(zhì)而存在的,物質(zhì)會(huì)由于意識(shí)的發(fā)展而發(fā)展。雙方是相互依存、缺一不可的!因此算法依賴于具體的數(shù)據(jù)結(jié)構(gòu), 數(shù)據(jù)結(jié)構(gòu)關(guān)系到算法的選擇與效益!在面向?qū)ο缶幊讨校菍⑺惴ㄅc數(shù)據(jù)結(jié)構(gòu)看成一個(gè)整體,稱做對(duì)象。Wirth公式也就擴(kuò)展為:“對(duì)象=算法+數(shù)據(jù)結(jié)構(gòu)”、“程序=對(duì)象1+對(duì)象2+……+對(duì)象n”。過程化程序通俗來說就是告訴計(jì)算機(jī)做什么?怎么做?計(jì)算機(jī)要把做出來的結(jié)果展示給我們……人機(jī)交互性和操作性決定了采用案例教學(xué)的可行性和有效性。過程化編程強(qiáng)調(diào)三種基本結(jié)構(gòu),所以這里選擇了分支結(jié)構(gòu)與循環(huán)結(jié)構(gòu)的兩個(gè)有代表性的典型案例。由于算法的靈活性及多樣性決定了程序的豐富多彩,因此每個(gè)案例我們都給出了三種解決方案。
一、分支結(jié)構(gòu)案例及解決方案
案例1:白云服裝公司經(jīng)營(yíng)套服,同時(shí)也單件出售,如果整套買入服裝,一次性購(gòu)買的多于50套(含50套),每套為80元;如果一次性購(gòu)買的不足50套,每套90元;如果只買進(jìn)上衣,每件60元;如果只買進(jìn)褲子,每條45元;請(qǐng)輸入需要購(gòu)買的上衣和褲子的件數(shù),計(jì)算出應(yīng)付金額。[2]
問題分析:充分理解題意找到解決思路:輸入上衣數(shù)量(用c表示)和褲子數(shù)量(用t表示),哪個(gè)數(shù)量小,哪個(gè)按成套數(shù)算,剩下的部分按單件算。例如:c=65,t=60,應(yīng)付金額應(yīng)當(dāng)是m=60*80+(65-60)*60=5100而不是m=65*60+60*45=6600或其他。這里顧客要熟悉定價(jià)規(guī)則,防止商家往高處算。要讓同學(xué)們知道,在過程化編程中,我們要尋找解決方案,然后將這個(gè)解決思路用某種語言寫成代碼讓計(jì)算機(jī)執(zhí)行。顯然這是一個(gè)多分支結(jié)構(gòu),多分支結(jié)構(gòu)編程關(guān)鍵是把各種可能性都要考慮到。通過方法1中的程序代碼注釋部分可看出編程思路。
方法1:
#include
void main()
{
int c, t; /*變量c代表買上衣的件數(shù),t代表買褲子的件數(shù)*/
int m; /*變量m表示應(yīng)付金額*/
cout << "請(qǐng)輸入你需要買的上衣和褲子的件數(shù):\n";
cin >> c >> t; /*輸入需要買的上衣和褲子的件數(shù)*/
if (c == t) /*成套買*/
{
if (c >= 50)
m = c * 80; /*買50套以上,每套80元*/
else
m = c * 90; /*買50套以下,每套90元*/
}
else /*不成套買*/
{
if (c > t) /*買的上衣比褲子多*/
if (t >= 50)
m = t * 80 + (c - t) * 60;/*多于50套,成套部分按每套80元算,單件另算*/
else
m = t * 90 + (c - t) * 60;/*少于50套,成套部分按每套90元算,單件另算*/
else /*買的褲子比上衣多*/
if (c>=50)
m = c * 80 + (t - c) * 45;/*多于50套,成套部分按每套80元算,單件另算*/
else
m = c * 90 + (t - c) * 45;/*少于50套,成套部分按每套90元算,單件另算*/
}
cout << "\n應(yīng)付金額是: " << m << "\n";
}
運(yùn)行結(jié)果如下:
c=65,t=60時(shí),應(yīng)付金額是5100,程序計(jì)算結(jié)果和前面手工計(jì)算結(jié)果一致。
在上課過程中要引導(dǎo)同學(xué)們積極思考,雖然問題分析、程序代碼、程序運(yùn)行結(jié)果都已經(jīng)出來,但我們考慮一下定價(jià)臨界值問題:買50套服裝花費(fèi)50*80=4000元,買49套服裝花費(fèi)49*90=4410元……買45套服裝花費(fèi)45*90=4050元,買44套服裝花費(fèi)44*90=3960元。結(jié)果很有意思:買50套服裝比買49套服裝便宜410元……比買45套服裝便宜50元、僅僅比買44套服裝貴40元。定價(jià)規(guī)則出了問題?商家需要修改定價(jià)規(guī)則嗎?
至此問題已圓滿解決,作為思維拓展我們還要考慮其他思路的解決方法。后面的方法2和方法3教科書上一般不會(huì)有,有一定的創(chuàng)新性。但有了方法1的基礎(chǔ),容易理解這兩種方法。讀者可仔細(xì)閱讀、細(xì)心體會(huì)。
方法2:
#include
int main()
{ /* d1表示單件上衣數(shù),d2表示單件褲子數(shù)。 */
int c,t,ts,d1=0,d2=0,m; /* ts表示c與t的最小值,構(gòu)成套數(shù)。 */
printf("請(qǐng)輸入你需要買的上衣件數(shù):\n");
scanf("%d",&c);
printf("請(qǐng)輸入你需要買的褲子件數(shù):\n");
scanf("%d",&t);
if(c>t)
{
ts=t;
d1=c-t;
}
else
{
ts=c;
d2=t-c;
}
if(ts>=50)
m=ts*80+d1*60+d2*45;
else
m=ts*90+d1*60+d2*45;
printf("應(yīng)付金額是: %d\n",m);
return 0;
}
這里if(ts>=50) m=ts*80+d1*60+d2*45; else m=ts*90+d1*60+d2*45;是個(gè)關(guān)鍵的式子,找到了規(guī)律也就找到了簡(jiǎn)化的方向,有了思路也就有了方法!為保證思維效果的完備性,程序運(yùn)行結(jié)果分為以下六種有意思的情形:
1、50套以上(含50套),上衣多于褲子。
2、50套以上(含50套),上衣少于褲子。
3、50套以上(含50套),上衣和褲子相等(成套)。
4、50套以下,上衣多于褲子。
5、50套以下,上衣少于褲子。
6、50套以下,上衣和褲子相等(成套)。
方法3:
#include "stdio.h"
int main()
{ int c,t,ts,m;
printf("input c,t:\n");
scanf("%d%d",&c,&t);
ts=c m=(ts>=50)?(ts*80):(ts*90); m=m+(c-ts)*60+(t-ts)*45; printf("m=%d\n",m); getch(); return 0; } 注:m=(ts>=50)?(ts*80):(ts*90); 與m=m+(c-ts)*60+(t-ts)*45;這兩個(gè)語句可合成一個(gè)語句: m=( (ts>=50)?(ts*80):(ts*90) )+(c-ts)*60+(t-ts)*45;效果是一樣的。 整理思路我們會(huì)發(fā)現(xiàn),方法2是方法1的簡(jiǎn)化,方法3是方法2的進(jìn)一步簡(jiǎn)化,用條件表達(dá)式代替了if語句(代替的前提是雙分支中對(duì)同一變量賦值)。規(guī)律決定了簡(jiǎn)化的方向!需要“畫龍點(diǎn)睛”的是:付款金額是成套數(shù)的錢加上單件的錢,套數(shù)是上衣數(shù)與褲子數(shù)的最小值,單件數(shù)用上衣數(shù)或褲子數(shù)減去套數(shù)即可(其中必有一個(gè)為0)。 二、循環(huán)結(jié)構(gòu)案例及解決方案 案例2:從鍵盤輸入兩個(gè)自然數(shù),求出它們的最大公約數(shù)與最小公倍數(shù),輸出結(jié)果。[3] 問題分析:根據(jù)代數(shù)學(xué)的知識(shí),求最大公約數(shù)的算法可以采用“輾轉(zhuǎn)相除法”,這就找到了解決該問題的思路:對(duì)于兩個(gè)自然數(shù)a和b,若a=b,則其最大公約數(shù)和最小公倍數(shù)都是a;若a與b不相等,不妨設(shè)大數(shù)是a,分為以下兩種情況:1、用a除以b得到余數(shù)r,若r=0,則b(小數(shù))就是兩數(shù)的最大公約數(shù)。2、若r不等于0,則令a=b,b=r,再轉(zhuǎn)去執(zhí)行第1種情況。用數(shù)學(xué)式子表達(dá)如下: a=q*b+r, b=q1*r+r1, r=q2*r1+r2, ……,(a,b)=(b,r)=(r,r1)=(r1,r2)=…=(rn,0)= rn。rn即為所求結(jié)果。其中(m,n)代表m和n的最大公約數(shù)。 “輾轉(zhuǎn)相除”是一個(gè)重復(fù)過程,可用循環(huán)實(shí)現(xiàn),循環(huán)的前提是r,r1,r2…不等于0。 最小公倍數(shù)的算法是:a與b最小公倍數(shù)= (a*b)/( a與b最大公約數(shù))。 方法1: #include void main() { int a,b,r,sa,sb; cout<<"請(qǐng)輸入兩個(gè)正整數(shù):"< cin>>a>>b; sa=a; sb=b; if(a {r=a;a=b;b=r;} r=a%b; while(r!=0) {a=b;b=r;r=a%b;} cout<<"最大公約數(shù):"< cout<<"最小公倍數(shù):"< } 注:程序中語句if(a 下面我們考慮將“輾轉(zhuǎn)相除法”求最大公約數(shù)寫成一個(gè)函數(shù),通過主函數(shù)調(diào)用此函數(shù)得到解決方案。 方法2: #include int gys(int a,int b)/* 求兩數(shù)的最大公約數(shù)*/ { int r; while(b!=0) { r=a%b; a=b; b=r; } return a; } void main() //主函數(shù) {
int x,y,m,n;/* x,y為任意兩數(shù),m,n為最大公約數(shù)與最小公倍數(shù)*/
cout<<"請(qǐng)輸入兩個(gè)自然數(shù):"< cin>>x>>y; m=gys(x,y);n=(x*y)/m; cout<<"最大公約數(shù)是:"< } 前面兩種方法通常是數(shù)學(xué)基礎(chǔ)好的編程者的思路,也是大多數(shù)教科書作者能想到的方法。但不知道“輾轉(zhuǎn)相除法”的編程者怎么辦?不知道也沒關(guān)系,不知道就不會(huì)有“已知障礙”,沒有“已知障礙”就可能有所創(chuàng)新!下面的方法3就比較新穎,用“循環(huán)篩選法”思路可找到最大公約數(shù)和最小公倍數(shù)。編程的“循環(huán)篩選法”是本人自行命名的,其他文獻(xiàn)不一定能查到。古代難題“百錢買百雞”就是用“循環(huán)篩選法”編程解決的一個(gè)典型案例,但教科書上提的是“用循環(huán)求解不定方程”。 方法3: #include "iostream" using namespace std; int zxgbs(int a,int b)//求出最小公倍數(shù) { int i,j; i=a; if(b>i) i=b; for(j=i;;j++) { if(j%a==0 && j%b==0) break; } return j; } int zdgys(int a,int b)//求出最大公約數(shù) { int i,j; i=a; if(b i=b; for(j=i;;j--) { if(a%j==0 && b%j==0) break; } return j; } void main()//主函數(shù) { int x,y; cout<<"請(qǐng)輸入兩個(gè)自然數(shù):"< cin>>x>>y; cout< cout< } 三、在戰(zhàn)爭(zhēng)中學(xué)習(xí)戰(zhàn)爭(zhēng) 在編程中學(xué)習(xí)編程 “先投入戰(zhàn)斗,再去想解決的辦法?!保@是拿破侖的名言;“在戰(zhàn)爭(zhēng)中學(xué)習(xí)戰(zhàn)爭(zhēng)”,這是毛澤東的名言。兩位大人物表達(dá)的內(nèi)容是一致的。編程是對(duì)客觀事物的認(rèn)識(shí)和描述,自然界與人類社會(huì)的復(fù)雜性決定了我們必須深入其中才能正確地認(rèn)識(shí)和描述。人類思維的局限性也決定了我們學(xué)習(xí)編程要從簡(jiǎn)單到復(fù)雜循序漸進(jìn)地展開。所以編程和戰(zhàn)爭(zhēng)一樣,必須身體力行投入進(jìn)去、不斷地學(xué)習(xí)、總結(jié)和提高。高等院校計(jì)算機(jī)類專業(yè)開設(shè)的編程類課程主要有:C語言程序設(shè)計(jì)、數(shù)據(jù)結(jié)構(gòu)、算法設(shè)計(jì)與分析、數(shù)據(jù)庫(kù)、C++面向?qū)ο蠓治雠c設(shè)計(jì)、C#編程、JAVA編程、匯編語言、嵌入式編程、軟件工程等。前面的程序就是用C和C++編寫的。一個(gè)優(yōu)秀的程序員除了學(xué)好上述課程、打好扎實(shí)的基礎(chǔ)外,需要不斷地與時(shí)俱進(jìn)、掌握新知識(shí)、在實(shí)踐中鍛煉、提出問題和解決問題。 綜上所述,學(xué)習(xí)編程采用案例教學(xué)方式是非常適宜的。有關(guān)這方面的探討文章較多,我們這里主要是通過典型案例的多種解決方案,注重理清編程思路、分析編程方法,尋找思考過程的蹤跡。但這些只是基礎(chǔ)的編程訓(xùn)練,在信息瞬息萬變、技術(shù)突飛猛進(jìn)的今天,程序員之路“痛并快樂著”。 參考文獻(xiàn): [1]譚浩強(qiáng) C程序設(shè)計(jì)(第四版)[M]清華大學(xué)出版社2010年 [2]王超 C++程序設(shè)計(jì) [M]地質(zhì)出版社 2006年 [3]于帆 面向?qū)ο蟪绦蛟O(shè)計(jì)C++教程 [M]科學(xué)出版社 2009年