焦華



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