zoukankan      html  css  js  c++  java
  • 劣质代码评析——猜数字问题(下)

    前文链接:劣质代码评析——猜数字问题(上)
    【重构】

      这个问题的解决并不复杂,最多只需要三个步骤:

    1.  生成无重复数字的四位整数;
    2.  重复猜数字最多10次,猜中则宣布胜利,程序结束;
    3.  10次没猜中,宣布失败,程序结束。

      用C语言描述这个过程应该是这样:

    #define TIMES 10  //最多10次 
    int main( void )
    {
        int  count;
        //1. 生成无重复数字的四位整数;
        for( count = 0 ; count < TIMES ; count ++ )  //2. 重复猜数字最多10次,
        {
             //猜中则宣布胜利,程序结束;
        }
        //3. 10次没猜中,宣布失败,程序结束。  
        return 0;  
    }
    

       现在认真审视一下这个结构,思考一下它有无漏洞。这件事情很值得去做,千万不要错在起跑线上,否则后面的工作可能都是无用功。
      没有什么漏洞吧?得到这个结论并不困难,因为这本身就是一个简单的问题。
      接下来补充完善这个代码。
      写代码可以从前写到后也可以从后向前写,没有一定之规。可以先写难写的部分也可以先完成容易的部分。下面的代码完成了较为简单的部分。

    #include <stdio.h>
    
    #define TIMES 10  //最多10次 
    
    int main( void )
    {
        int  count;
        //1. 生成无重复数字的四位整数;
        for( count = 0 ; count < TIMES ; count ++ )  //2. 重复猜数字最多10次
        {
             //猜数字
             //if( 猜中 ) 
             {
                   puts("恭喜你猜对了");     //宣布胜利
                   return 0;                 //程序结束;
    
             }
        }
        //3. 10次没猜中
        printf("连续%d次你都没猜中,抱歉,游戏结束\n",TIMES); //宣布失败
        return 0;                                             //程序结束。
    }
    

       这个代码尽管没有完成全部功能,但却已经可以进行测试。测试之后继续完善
      //1. 生成无重复数字的四位整数;
    部分。
      这部分首先应该到考虑这个四位整数的存储问题。考虑到后面的算法需要按位进行比较,因此将其数据结构选择为

    #define NUM 4     //正整数位数
    
    int main( void )
    {
        int  dig[NUM]; 
        /*……*/
    }
    
    

     即按位分别存储。
      由于需要程序模拟随机效果,所以需要首先初始化种子数(seed)。否则程序每次运行的结果都将一样:

    #include <stdlib.h> 
    #include <time.h>
    
    srand( (unsigned) time( NULL ) );
    

       接下来要做的事情是利用rand()函数生成一个各位不同的四位伪随机数,并将其分解然后存入dig数组,这事情不可能一蹴而就,所以这里大而化之地把这些任务交给一个函数完成

    generate( dig , NUM );
    

       这样,代码就演化成为:

    #include <stdio.h>
    #include <stdlib.h> 
    #include <time.h>
    
    #define TIMES 10  //最多10次 
    #define NUM 4     //正整数位数
    
    int main( void )
    {
        int  dig[NUM]; //存放四位整数 
        int  count;
        //1. 生成无重复数字的四位整数;
        srand( (unsigned)time( NULL ) ); //初始化seed 
        generate( dig , NUM );           //生成无重复数字的四位整数,分解存入dig数组 
        
        for( count = 0 ; count < TIMES ; count ++ )  //2. 重复猜数字最多10次,
        {
             //猜数字
             //if( 猜中 ) 
             {
                   puts("恭喜你猜对了");     //宣布胜利
                   return 0;                 //程序结束;
    
             }
        }
        //3. 10次没猜中
        printf("连续%d次你都没猜中,抱歉,游戏结束\n",TIMES); //宣布失败
        return 0;                                             //程序结束。
    }
    

       generate()函数首要的任务是生成一个四位伪随机数,这可以通过rand() % ( 10000 - 1000 - 1 ) + 1000 这个表达式实现。之后需要检查这个四位数是否有重复数字,如果没有重复则将其分解存入数组。大体上这样

    do
    {
       生成一个四位数
    }
    while(四位数有重复数字);
    
         分解存储;
    

      但是为了更容易地判断四位数是否有重复数字,一个技巧性的写法是

    do
    {
       生成一个四位数;
       分解存储;
    }
    while(四位数有重复数字);
    

       因此,generate()的这个函数的原型及定义为

    #define MIN_5  10000
    #define MIN_4  1000
    #define TRUE  1
    #define FALSE   0
    
    void generate( int [] , int );
    
    void generate( int d[] , int n )
    {
         
         do
         {
             int temp = rand() % ( MIN_5 - MIN_4 - 1 ) + MIN_4 ; //生成一个四位数      
             resolve( temp , d , n );                           //分解存储; 
         }
         while( be_reduplicate( d , n )  ==  TRUE );  //四位数有重复数字
    }
    

       其中的resolve()函数的作用是将一个正整数分解并按次序存入数组:

    #define TEN   10
    
    void resolve( int ,  int [] , int );
    void resolve( int t ,  int d[] , int n )
    {
    
         while( n-- > 0 )
         {
             d[n] = t % TEN ;
             t /= TEN ;   
         }
    }
    

      be_reduplicate()函数判断四位数字中是否有重复,如果有重复数字则再调用rand()重新生成。


    int be_reduplicate( int [] , int ); int be_reduplicate( int d[] , int n ) { int i; for( i = 0 ; i < n - 1 ; i ++ ) { int j ; for( j = i + 1 ; j < n ; j ++ ) if( d[i] == d[j] ) return TRUE ; } return FALSE ; }

        至此,//1. 生成无重复数字的四位整数;部分功能全部完成。下面继续完成
      //2. 重复猜数字最多10次,
    中尚未完成的部分。
      “//猜数字”的功能要求用户输入所猜的数,因此需要为这个输入的数据预备存储空间,所以

    int g_dig[NUM];
    

       “//猜数字”这个过程本身可以用一个函数实现

    void guess ( int [] , int ) ;
    int main( void ) 
    { 
     /*……*/
       {
            int g_dig[NUM];
            
            guess ( g_dig , NUM ) ; //猜数字
        }
     /*……*/
    }
    void guess ( int d[] , int n )
    {
         int i ;
         printf("输入你猜的数:");
         for( i = 0 ; i < n ; i ++ )
              scanf("%1d" , d + i );     
    }
    

       guess ()中的scanf("%1d" , d + i );是一种较为简洁的实现方法,但是这种方法的健壮性并不够。一旦用户输入了非十进制数字的非空白字符,就会出现问题。这个不足将在后面予以改进。
      最后编写“//if( 猜中 )”部分的代码。是否“猜中”可由一函数判断

    if( be_same(dig , g_dig , NUM ) == TRUE )
    

       其中be_same()函数为

    int be_same ( int [] , int [] , int );
    int be_same ( int ori[] , int spe[] , int n ) 
    {
        int A = 0 , B = 0 ;
        int i ;
        
        for( i = 0 ; i < n ; i ++ )
             if( spe[i] == ori[i] )
                 A++ ;
             else
                 if( be_inside ( spe[i] , ori , n ) == TRUE )
                     B++;
    
        if( A == n )
           return TRUE ;
           
        printf("%dA%dB\n",A,B);         //输出几A几B
        return FALSE ;   
    }
    

       其中的be_inside()函数用于判断第一个参数dig是否在后面的d数组中

    int be_inside ( int  , int [] , int );
    int be_inside ( int dig , int d[] , int n )
    {
        while( n-- > 0 )
           if( dig == d[n] )
               return TRUE;
    
        return FALSE;
    }
    
    

      至此,问题解决。下面给出这一问题的完整代码。这个代码在前面的基础上对guess ()的健壮性做了改进,另外添加了在10次没猜中时输出被猜的数的代码。

    #include <stdio.h>
    #include <stdlib.h> 
    #include <time.h>
    
    #define TIMES  10    //最多10次 
    #define NUM    4     //正整数位数
    #define MIN_5 	10000
    #define MIN_4 	1000
    #define TRUE 	1
    #define FALSE  0
    #define TEN    10
    
    void generate( int [] , int );
    void resolve( int ,  int [] , int );
    int be_reduplicate( int [] , int );
    void guess ( int [] , int ) ;
    int be_same ( int [] , int [] , int );
    int be_inside ( int  , int [] , int );
    void out( int [] , int );
    
    int main( void )
    {
        int  dig[NUM]; //存放四位整数 
        int  count;
        //1.	生成无重复数字的四位整数;
        srand( (unsigned)time( NULL ) ); //初始化seed 
        generate( dig , NUM );           //生成无重复数字的四位整数,分解存入dig数组 
        
        for( count = 0 ; count < TIMES ; count ++ )  //2.	重复猜数字最多10次,
        {
             
             int g_dig[NUM];         //预备数据存储空间
             
             guess ( g_dig , NUM ) ; //猜数字
             if( be_same(dig , g_dig , NUM ) == TRUE )	//if( 猜中 ) 
             {
                   puts("恭喜你猜对了");     //宣布胜利
                   system("PAUSE");return 0; //程序结束;
    
             }
        }
        //3.	10次没猜中
        printf("那个数是:");                                                      
        out( dig , NUM );                                     //输出被猜的数  
        printf("连续%d次你都没猜中,抱歉,游戏结束\n",TIMES); //宣布失败
        return 0;                                             //程序结束。
    }
    
    void out( int d[] , int n )
    {
         int i ;
         for( i = 0 ; i < n ; i ++ )
              printf("%d " , d[i]);
         putchar('\n');
    }
    
    int be_inside ( int dig , int d[] , int n )
    {
        while( n-- > 0 )
           if( dig == d[n] )
               return TRUE;
    
        return FALSE;
    }
    
    int be_same ( int ori[] , int spe[] , int n ) 
    {
        int A = 0 , B = 0 ;
        int i ;
        
        for( i = 0 ; i < n ; i ++ )
             if( spe[i] == ori[i] )
                 A++ ;
             else
                 if( be_inside ( spe[i] , ori , n ) == TRUE )
                     B++;
    
        if( A == n )
           return TRUE ;
           
        printf("%dA%dB\n",A,B);     //输出几A几B
        return FALSE ;   
    }
    
    void guess ( int d[] , int n )
    {
         int i ;
         printf("输入你猜的数:");
         for( i = 0 ; i < n ; i ++ )
         {
              scanf("%*[^0123456789]"); //读非十进制数字字符 
              scanf("%1d" , d + i );
         }
              
    }
    
    int be_reduplicate( int d[] , int n )
    {
        int i;
        for( i = 0 ; i < n - 1 ; i ++ )
        {
            int j ;
            for( j = i + 1 ; j < n ; j ++ )
                 if( d[i] == d[j] )
                     return TRUE ;
        }
        
        return FALSE ;
    }
    
    void resolve( int t ,  int d[] , int n )
    {
    
         while( n-- > 0 )
         {
             d[n] = t % TEN ;
             t /= TEN ;   
         }
    }
    
    void generate( int d[] , int n )   
    {
         
         do
         {
             int temp = rand() % ( MIN_5 - MIN_4 - 1 ) + MIN_4 ; //生成一个四位数      
              resolve( temp , d , n );                            //分解存储; 
         }
         while( be_reduplicate( d , n )  ==  TRUE );  //四位数有重复数字
    }
    

      

  • 相关阅读:
    大数据综合项目DocCloud之需求分析与功能实现详细(续更)
    CDH5 Hadoop集群完全离线安装步骤总结
    Phoenix安装与使用文档超详细步骤
    HBASE的存储缺陷以及预分区的解决办法
    ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
    Hbase 的javaAPI基本操作用 在idea上的实现
    android SimpleCursorAdapter的使用
    android TextView 添加下划线
    Android WebView的使用
    Android SDK下载和更新失败的解决方法!!!
  • 原文地址:https://www.cnblogs.com/pmer/p/2731910.html
Copyright © 2011-2022 走看看