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

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

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

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

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

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

   这个代码尽管没有完成全部功能,但却已经可以进行测试。测试之后继续完善
  //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 );

   这样,代码就演化成为:

   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()重新生成。

 

<br>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次没猜中时输出被猜的数的代码。

  

posted @   garbageMan  阅读(1861)  评论(7编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· SQL Server 2025 AI相关能力初探
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
点击右上角即可分享
微信分享提示