劣质代码评析——猜数字问题(下)

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

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

  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 );  //四位数有重复数字
}

  

原文地址:https://www.cnblogs.com/pmer/p/2731910.html