焦华
摘要:过程化编程是面向对象编程的基础,它的规律和特点适合采用案例教學、适合进行思维训练。通过对典型案例的剖析,在给出了多种解决方案的过程中,本文的亮点是哲学视野下的思维拓展和思维创新。作为基础编程训练,本文对《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年