zoukankan      html  css  js  c++  java
  • 内裤外穿——错位及不伦不类

          内裤这种东西是穿在外裤里面的,穿在外面就成了一种“错位”。错位的结局必然是非驴非马、不伦不类。不要以为这种事情不会在代码中发生,代码中同样可能存在错位和不伦不类。
    题目:用递归方法求n!。

    #include <stdio.h>
    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);
    return0;
    }
    int fac(int n)
    {
    int f;
    if(n<0)
    printf(
    "n<0,data error!");
    elseif(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()函数:

    int main( void )
    {
      int n;
      //输入n
      //计算n!
      //输出n! 
       
       return 0;
    }
    

          训练有素的程序员应该能看出这个总体思路的逻辑毛病,因为计算n!的前提是n!存在且能够计算n!(n比较大时就完全成了另一个问题,这个问题这里不打算讨论。这里假设n不是很大,n!可用简单的办法求得)。历史上,人类曾花了2000多年的时间研究用尺规三等分角的方法,可最后才发现这种方法根本不存在,这个教训可谓深矣。如果不理解计算的前提是可以计算,不管学过多少数学,都算白学。
          当然,无论是谁,思虑不周都是可能的。最初没想到而后来想到也不算是罪大恶极。但问题是后来一旦想到了就一定要返回main()重新审视总体构思,否则一旦顶层存在问题,代码再怎么写也是错的。俗话说,上梁不正下梁歪。人们往往能看到社会风气的败坏,但却很少思考败坏的源头何在,这样是解决不了问题的。所以,在这种情况下必须重新修正main()函数,才能继续后面的工作,而绝不能急着完成代码细节。

    int main( void )
    {
      int n;
      //输入n
      if ( n < 0 )
    {//错误处理
    }
      else 
    {
         //计算n!
         // 输出n!  
    }
       
       return 0;
    }
    

          总体构思无误后,才有可能对fac()提出正确的功能要求。fac()只需要针对可计算的整数求阶乘并返回这个值,显然fac()的形参应该是unsigned类型,返回值也以unsigned类型为好,因为计算范围比返回值为int的更大些。把这些考虑用代码表达出来就是:

    unsigned fac(unsigned);
    int main( void )
    {
      int n;
      //输入n
      if ( n < 0 )
    {//错误处理
    }
      else 
    {
         //计算n!
         // 输出n!  
    }
       
       return 0;
    }
    //fac():计算n!并返回
    unsigned fac(unsigned n)
    {
    }
    

          到了这一步,后面的事情就是简单的力气活了。

    #include <stdio.h>
    
    unsigned fac(unsigned);
    
    int main( void )
    {
      int n;
    
      //输入n
      printf("请输入一个正整数:");
      scanf("%d",&n);
    
      if ( n < 0 )
        printf("该数小于0,无法计算!\n");            //错误处理
      else 
        printf("%d!=%u\n" , n , fac( (unsigned)n ) );//计算,输出n!
    
       return 0;
    }
    
    //fac():计算n!并返回
    unsigned fac(unsigned n)
    {
       if ( n == 0U )
          return 1U ;
       else
          return n * fac( n - 1U ) ;
    }
    

    总结:
    1.结构化程序设计的一个核心理念就是层次,自顶向下必须先建立层次这个概念才可能实现。
    2.自顶向下就是由高层到低层,先粗后细,先大节后小节。切忌在各个不同的层次间玩“穿越”,否则就会导致层次错位,就如同把内裤套在外裤外面,结果必然不伦不类。
    3.修改程序也必须遵循这样的次序,上层的问题切莫企图在下层修补;如果是上层存在问题,下层无论怎样忙活都无济于事。就如同老板弱智,员工勤恳一样,只会认认真真地把事情弄糟而已。

  • 相关阅读:
    Python Django开发遇到的坑(版本不匹配)
    Mysql安装与问题合集
    git branch -r查看不了远程所有分支
    angularJS使用$http请求下载excel表格
    遍历formData对象数据
    按需使用CryptoJS之AES加密(CFB)模式
    git之创建、删除分支
    git pull时报错:Access Denied (拒绝访问)
    angularJS监听数据变化
    Angular-ui-router入门
  • 原文地址:https://www.cnblogs.com/pmer/p/2173183.html
Copyright © 2011-2022 走看看