Chinaunix首页 | 论坛 | 博客
  • 博客访问: 136983
  • 博文数量: 22
  • 博客积分: 1326
  • 博客等级: 中尉
  • 技术积分: 258
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-02 00:52
文章分类
文章存档

2012年(1)

2011年(21)

分类: C/C++

2011-09-10 15:10:58

    内裤这种东西是穿在外裤里面的,穿在外面就成了一种“错位”。错位的结局必然是非驴非马、不伦不类。不要以为这种事情不会在代码中发生,代码中同样可能存在错位和不伦不类。
题目:用递归方法求n!。
#include
int main()
{  int fac(int n);
   int n;
   int y;
   printf("input an integer number:");
   scanf("%d",&n);
   y=fac(n);
   printf("%d!=%d\n",n,y);
   return 0;
}
int fac(int n)
{
  int f;
  if(n<0)
    printf("n<0,data error!");
  else if(n==0||n==1)
    f=1;
  else f=fac(n-1)*n;
  return(f);

————谭浩强 ,《C程序设计》(第四版),清华大学出版社,2010年6月,p188
    这段代码,当用户输入一个负整数后,main()函数会把这个错误的数据传给fac()函数,fac()函数会在输出设备上输出“n<0,data error!”,然而自相矛盾的是fac()函数还会把一个不确定的垃圾值错误地返回给main()函数,然而main()并不知道这是个错误的返回值,因而“很傻很天真”地输出这个毫无意义的返回值。最后的运行结果类似下面这样:
input an integer number:-1
n<0,data error!-1!=-1
    一方面它说数据错误,但同时又告诉你“-1!=-1”。显然,这是一个不伦不类的结果。和内裤外裤同时暴露有得一拼。
    然而,仅仅揭露这种荒谬是远远不够的,更重要的事情在于如何避免这种荒谬。而想避免这种荒谬就必须揭示产生这种荒谬的原因。
    先考察一下样本代码的思路。main()函数主要由三个部分组成:输入n;计算n!;输出。而fac()函数的想法是如果n是负数,无法计算,输出“data error!”,否则如果n为0或1则f=1;否则计算“f=fac(n-1)*n”;最后返回f值。
    问题就出在这里,不难发现fac()这个函数的功能几乎根本无法总结并描述,即使描述出来也是错误的,因为如果n是负数的话它最后同样也返回一个值。这种函数的功能是错乱的,造成这种错乱的原因在于,在构思main()的时候没有想到不是所有的整数都有阶乘(高等数学那么多存在性定理算是白学了),但是在写fac()又突然想到了,然而却不是回头重新考虑程序的总体思路,而是匆忙地把对负数的处理写在了fac()中,然而对负数的处理本应该在main()中进行,把这个处理写在fac()中就人为地制造出了一种“错位”,这就是结果不伦不类的根本原因。
    造成错位的另一种可能性是,事先根本就没有总体的思路(main()),在main()写到一半时去写fac(),在写fac()想到了对负数的处理问题于是顺手写出,写完fac()之后再回到main()继续写完剩余部分的代码。这种缺乏总体构思东一榔头西一棒子写到哪算哪的写代码方法,完全违背了结构化程序设计自顶向下的思想,写出漏洞百出的程序是顺理成章的结局。
    按照自顶向下的技术风格则不会产生这种问题。自顶向下要求首先构思main()函数:
  1. int main( void )
  2. {
  3.   int n;
  4.   //输入n
  5.   //
  6.   //
  7.    
  8.    return 0;
  9. }
    训练有素的程序员应该能看出这个总体思路的逻辑毛病,因为计算n!的前提是n!存在且能够计算n!(n比较大时就完全成了另一个问题,这个问题这里不打算讨论。这里假设n不是很大,n!可用简单的办法求得)。历史上,人类曾花了2000多年的时间研究用尺规三等分角的方法,可最后才发现这种方法根本不存在,这个教训可谓深矣。如果不理解计算的前提是可以计算,不管学过多少数学,都算白学。
    当然,无论是谁,思虑不周都是可能的。最初没想到而后来想到也不算是罪大恶极。但问题是后来一旦想到了就一定要返回main()重新审视总体构思,否则一旦顶层存在问题,代码再怎么写也是错的。俗话说,上梁不正下梁歪。人们往往能看到社会风气的败坏,但却很少思考败坏的源头何在,这样是解决不了问题的。所以,在这种情况下必须重新修正main()函数,才能继续后面的工作,而绝不能急着完成代码细节。
  1. int main( void )
  2. {
  3.   int n;
  4.   //输入n
  5.   if ( n < 0 )
  6. {//错误处理
  7. }
  8.   else
  9. {
  10.      //
  11.      //
  12. }
  13.    
  14.    return 0;
  15. }
    总体构思无误后,才有可能对fac()提出正确的功能要求。fac()只需要针对可计算的整数求阶乘并返回这个值,显然fac()的形参应该是unsigned类型,返回值也以unsigned类型为好,因为计算范围比返回值为int的更大些。把这些考虑用代码表达出来就是:
  1. unsigned fac(unsigned);
  2. int main( void )
  3. {
  4.   int n;
  5.   //输入n
  6.   if ( n < 0 )
  7. {//错误处理
  8. }
  9.   else
  10. {
  11.      //
  12.      //
  13. }
  14.    
  15.    return 0;
  16. }
  17. //fac():计算n!并返回
  18. unsigned fac(unsigned n)
  19. {
  20. }
    到了这一步,后面的事情就是简单的力气活了。
  1. #include <stdio.h>
  2. unsigned fac(unsigned);
  3. int main( void )
  4. {
  5.   int n;
  6.   //输入n
  7.   printf("请输入一个正整数:");
  8.   scanf("%d",&n);
  9.   if ( n < 0 )
  10.     printf("该数小于0,无法计算!\n"); //错误处理
  11.   else
  12.     printf("%d!=%u\n" , n , fac( (unsigned)n ) );//计算,
  13.    return 0;
  14. }
  15. //fac():计算n!并返回
  16. unsigned fac(unsigned n)
  17. {
  18.    if ( n == 0U )
  19.       return 1U ;
  20.    else
  21.       return n * fac( n - 1U ) ;
  22. }
总结:
1.结构化程序设计的一个核心理念就是层次,自顶向下必须先建立层次这个概念才可能实现。
2.自顶向下就是由高层到低层,先粗后细,先大节后小节。切忌在各个不同的层次间玩“穿越”,否则就会导致层次错位,就如同把内裤套在外裤外面,结果必然不伦不类。
3.修改程序也必须遵循这样的次序,上层的问题切莫企图在下层修补;如果是上层存在问题,下层无论怎样忙活都无济于事。就如同老板弱智,员工勤恳一样,只会认认真真地把事情弄糟而已。
阅读(3165) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~