zoukankan      html  css  js  c++  java
  • 劣质代码评析——《写给大家看的C语言书(第2版)》附录B之21点程序(五)

    0. #include <stdio.h>
    1. #include <time.h>
    2. #include <ctype.h>
    3. #include <stdlib.h>
    4. 
    5. #define BELL 'a'
    6. #define DEALER 0
    7. #define PLAYER 1
    8. 
    9. #define ACELOW 0
    10. #define ACEHIGH 1
    11. 
    12. int askedForName = 0;
    13. 
    14. 
    15. void dispTitle(void);
    16. void initCardsScreen(int cards[52],int playerPoints[2],
    17. int dealerPoints[2], int total[2], 
    18. int *numCards);
    19. int dealCard(int * numCards,int cards[52]);
    20. void dispCard(int cardDrawn,int points[2]);
    21. void totalIt(int points[2],int tatal[2],int who);
    22. void dealerGetsCard(int *numCards,int cards[52],
    23. int dealerPoints[2]);
    24. void playerGetsCard(int *numCards,int cards[52],
    25. int playerPoints[2]);
    26. char getAns(char mesg[]);
    27. void findWinner(int total[2]);
    28. 
    29. main()
    30. {
    31.    int numCards;
    32.    int cards[52],playerPoints[2],dealerPoints[2],total[2];
    33.    char ans;
    34.    
    35.    do 
    36.    { 
    37.       initCardsScreen(cards,playerPoints,dealerPoints,total, &numCards);
    38.       dealerGetsCard(&numCards,cards, dealerPoints);
    39.       printf("
    ");
    40.       playerGetsCard(&numCards,cards,playerPoints); 
    41.       playerGetsCard(&numCards,cards,playerPoints);
    42.       do
    43.       {
    44.          ans = getAns("Hit or stand (H/S)?");
    45.          if ( ans == 'H' )
    46.          { 
    47.             playerGetsCard(&numCards,cards,playerPoints);
    48.          }  
    49.       }
    50.       while( ans != 'S' );
    51.       
    52.       totalIt(playerPoints,total,PLAYER);
    53.       do
    54.       {
    55.          dealerGetsCard(&numCards,cards,dealerPoints);
    56.       }
    57.       while (dealerPoints[ACEHIGH] < 17 );
    58.       
    59.       totalIt(dealerPoints,total,DEALER);
    60.       findWinner(total); 
    61.       
    62.       ans = getAns("
    Play again(Y/N)?");  
    63.    }
    64.    while(ans=='Y');
    65.    
    66.    return 0;
    67. }
    68. 
    69. void initCardsScreen( int cards[52],int playerPoints[2],
    70.                       int dealerPoints[2], int total[2], 
    71.                       int *numCards )
    72. {
    73.    int sub,val = 1 ;
    74.    char firstName[15];
    75.    *numCards=52;
    76.    
    77.    for(sub=0;sub<=51;sub++)
    78.    {
    79.       val = (val == 14) ? 1 : val;
    80.       cards[sub] = val;
    81.       val++;  
    82.    }
    83.    
    84.    for(sub=0;sub<=1;sub++)
    85.    { 
    86.       playerPoints[sub]=dealerPoints[sub]=total[sub]=0;
    87.    }
    88.    dispTitle();
    89.    
    90.    if (askedForName==0)
    91.    { 
    92.       printf("What is your first name?");
    93.       scanf(" %s",firstName);
    94.       askedForName=1;
    95.       printf("Ok, %s,get ready for casino action!
    
    ",firstName);
    96.       getchar();
    97.    }
    98.    return;        
    99. }
    100. 
    101. void playerGetsCard(int *numCards,int cards[52],int playerPoints[2])
    102. {
    103.    int newCard;
    104.    newCard = dealCard(numCards, cards);
    105.    printf("You draw:");
    106.    dispCard(newCard,playerPoints);
    107. }
    108. 
    109. 
    110. void dealerGetsCard(int *numCards,int cards[52],int dealerPoints[2])
    111. {
    112.    int newCard;
    113.    newCard = dealCard(numCards,cards);
    114.    printf("The dealer draws:");
    115.    dispCard(newCard,dealerPoints);
    116. }
    117. 
    118. int dealCard(int * numCards,int cards[52])
    119. {
    120.    int cardDrawn,subDraw;
    121.    time_t t;
    122.    srand(time(&t));
    123.    subDraw = (rand()%(*numCards));
    124.    cardDrawn = cards[subDraw];
    125.    cards[subDraw] = cards[*numCards -1];
    126.    (*numCards)--;
    127.    return cardDrawn;
    128. }
    129. 
    130. void dispCard(int cardDrawn, int points[2])
    131. {
    132.    switch(cardDrawn)
    133.    {
    134.       case(11): printf("%s
    ","Jack");
    135.                 points[ACELOW] += 10;
    136.                 points[ACEHIGH] += 10;
    137.                 break;
    138.       case(12): printf("%s
    ","Queen");
    139.                 points[ACELOW] += 10;
    140.                 points[ACEHIGH] += 10;
    141.                 break;
    142.       case(13): printf("%s
    ","King");
    143.                 points[ACELOW] += 10;
    144.                 points[ACEHIGH] += 10;
    145.                 break;
    146.       default : points[ACELOW] += cardDrawn;
    147.                 if(cardDrawn==1)
    148.                 { 
    149.                    printf("%s
    ","Ace");
    150.                    points[ACEHIGH]+= 11;
    151.                 }
    152.                 else
    153.                 {  
    154.                   points[ACEHIGH]+=cardDrawn;
    155.                   printf("%d
    ",cardDrawn); 
    156.                 }
    157.    }
    158.    return ;
    159. }
    160. 
    161. void totalIt(int points[2],int total[2],int who)
    162. {
    163.    if ( (points[ACELOW] == points[ACEHIGH])
    164.       ||(points[ACEHIGH] < 21 ))
    165.    { 
    166.      total[who] = points[ACELOW];
    167.    }
    168.    else
    169.    { 
    170.        total[who] = points[ACEHIGH];
    171.    }
    172.    
    173.    if (who == PLAYER )
    174.    {
    175.       printf("You have a total of %d
    
    ", total[PLAYER]);
    176.    }
    177.    else
    178.    {
    179.        printf("The house stands with a total of %d
    
    ", 
    180.        total[DEALER]);
    181.    }
    182.    return;
    183. }
    184. 
    185. void findWinner(int total[2])
    186. {
    187.    if ( total[DEALER] ==  21 )
    188.    {
    189.        printf("The house wins.
    ");
    190.        return ;
    191.    }
    192.    if ( (total[DEALER] > 21) && (total[PLAYER] > 21) )
    193.    { 
    194.       printf("%s", "Nobody wins.
    ");
    195.       return ; 
    196.    }
    197.    if ((total[DEALER] >= total[PLAYER])&& (total[DEALER] < 21))
    198.    { 
    199.       printf("The house wins.
    ");
    200.       return ; 
    201.    }
    202.    printf("%s%c","You win!
    ",BELL);
    203.    return;
    204. }
    205. 
    206. char getAns(char mesg[])
    207. {
    208.    char ans;
    209.    printf("%s", mesg);
    210.    ans = getchar();
    211.    getchar();
    212.    return toupper(ans);
    213. }
    214. 
    215. void dispTitle(void)
    216. {
    217.    int i = 0 ;
    218.    while(i<25)
    219.    { 
    220.         printf("
    ");
    221.         i++; 
    222.    }
    223.    printf("
    
    *Step right up to the Blackjack tables*
    
    ");
    224.    return ;
    225. }
    View Code

      继续走查dispCard()函数:  

    20. void dispCard(int cardDrawn,int points[2]);
    
    130. void dispCard(int cardDrawn, int points[2])
    131. {
    132.    switch(cardDrawn)
    133.    {
    134.       case(11): printf("%s
    ","Jack");
    135.                 points[ACELOW] += 10;
    136.                 points[ACEHIGH] += 10;
    137.                 break;
    138.       case(12): printf("%s
    ","Queen");
    139.                 points[ACELOW] += 10;
    140.                 points[ACEHIGH] += 10;
    141.                 break;
    142.       case(13): printf("%s
    ","King");
    143.                 points[ACELOW] += 10;
    144.                 points[ACEHIGH] += 10;
    145.                 break;
    146.       default : points[ACELOW] += cardDrawn;
    147.                 if(cardDrawn==1)
    148.                 { 
    149.                    printf("%s
    ","Ace");
    150.                    points[ACEHIGH]+= 11;
    151.                 }
    152.                 else
    153.                 {  
    154.                   points[ACEHIGH]+=cardDrawn;
    155.                   printf("%d
    ",cardDrawn); 
    156.                 }
    157.    }
    158.    return ;
    159. }

       dispCard()函数的功能是显示抽到的牌的点数并计算抽牌者目前的总点数。这个函数最主要的毛病是可读性差,原因主要有两点,第一,在switch语句中蹩脚地嵌套了一句if语句,实际上这个switch语句可以这样写: 

    void dispCard(int cardDrawn,int points[]);
    
    void dispCard(int cardDrawn, int points[])
    {
       switch(cardDrawn)
       {
          case 11: puts("Jack");
                   points[ACELOW]  += 10;
                   points[ACEHIGH] += 10;
                   break;
          case 12: puts("Queen");
                   points[ACELOW]  += 10;
                   points[ACEHIGH] += 10;
                   break;
          case 13: puts("King");
                   points[ACELOW]  += 10;
                   points[ACEHIGH] += 10;
                   break;
          case 1  :puts("Ace");
                   points[ACELOW]  += 1 ;
                   points[ACEHIGH] += 11;
                   break;                
          default :printf("%d
    ",cardDrawn);
                   points[ACELOW]  += cardDrawn;
                   points[ACEHIGH] += cardDrawn;
                   break;
       }
    }
    

    显然要好看得多。
      第二,就是不应把输出牌面点数与计算点数放在这一个函数中同时完成,应该分为两个函数。 

    void dispCard(int cardDrawn);
    
    void dispCard(int cardDrawn)
    {
       switch(cardDrawn)
       {
          case 11: puts("Jack");
                   break;
          case 12: puts("Queen");
                   break;
          case 13: puts("King");
                   break;
          case 1  :puts("Ace");
                   break;                
          default :printf("%d
    ",cardDrawn);
                   break;
       }
    }
    
    void update(int cardDrawn,int points[]);
    
    void update(int cardDrawn, int points[])
    {
       switch(cardDrawn)
       {
          case 11: 
          case 12: 
          case 13: points[ACELOW]  += 10;
                   points[ACEHIGH] += 10;
                   break;
          case 1  :points[ACELOW]  += 1 ;
                   points[ACEHIGH] += 11;
                   break;                
          default :points[ACELOW]  += cardDrawn;
                   points[ACEHIGH] += cardDrawn;
                   break;
       }
    }
    

      这样代码更简单。
      现在回到main()函数,考察 dealerGetsCard(&numCards,cards, dealerPoints); 之后的代码。 

    40.       playerGetsCard(&numCards,cards,playerPoints); 
    41.       playerGetsCard(&numCards,cards,playerPoints);

       这两行的意思是在dealer(计算机)取得一张牌后,player开始抽牌。由于player至少要抽两张牌,所以连续两处调用playerGetsCard()函数。然而查看一下这个函数的定义就会发现: 

    24. void playerGetsCard(int *numCards,int cards[52],
    25. int playerPoints[2]);
    
    101. void playerGetsCard(int *numCards,int cards[52],int playerPoints[2])
    102. {
    103.    int newCard;
    104.    newCard = dealCard(numCards, cards);
    105.    printf("You draw:");
    106.    dispCard(newCard,playerPoints);
    107. }

       这个函数其实与dealerGetsCard()函数是一样的函数。同样的函数写了两次,无论如何都是很垃圾的写法。

      写代码有一条基本原则——要“拽”DRY(Don’t repeat yourself)(参见http://www.cnblogs.com/pmer/archive/2011/07/16/2108436.html)。相同的函数定义了两次就是不够“拽”。

      由于这两个函数基本相同,所以很容易合并为一个。它们的差别只在 

    105. printf("You draw:");

      

    114.    printf("The dealer draws:");

     这两句,可以通过为函数增加一个参数的办法统而为一,也可以把这两句从函数中直接剥离,这时playerGetsCard()和dealerGetsCard()这两个函数本身都没有存在的必要性了。即在main()中直接可调用dispCard()函数: 

    int main(void)
    {
        /*……*/
        do{
            /*……*/
            printf("The dealer draws:");
            dispCard ( dealCard( numCards , cards ) , dealerPoints ) ;
            
            printf("You draw:");
            dispCard ( dealCard( numCards , cards ) , playerPoints) ;
            /*……*/
        }
        while( getAns("
    Play again(Y/N)?") == 'Y' );  /*询问是否继续*/
        return 0;
    }
    

       main()中的 

    42.       do
    43.       {
    44.          ans = getAns("Hit or stand (H/S)?");
    45.          if ( ans == 'H' )
    46.          { 
    47.             playerGetsCard(&numCards,cards,playerPoints);
    48.          }  
    49.       }
    50.       while( ans != 'S' );

     的作用是让player输入。如果输入为H,则继续抽取一张牌;如果输入为S,则player取牌过程结束;如果输入其他字符,则继续提问。

      这段代码的逻辑有些复杂化。如果我写,大概会按下面方式写:

          do
          {
             char ans;
             
             ans = getAns("Hit or stand (H/S)?");
             
             if ( ans == 'H' )
                 playerGetsCard(&numCards,cards,playerPoints);
             
             if ( ans == ' S ' )
                break;
             
          }
          while( 1 );
    

     并且把它抽象为一个函数。 

  • 相关阅读:
    [usaco3.2.5]msquare
    [usaco3.2.4]ratios
    [usaco3.2.3]spin
    [文献记录] Few-shot Learning for Named Entity Recognition in Medical Text 医学文本中命名实体识别的小样本学习
    计算机保研经验分享
    文本处理、词频统计与Simhash生成文档指纹
    [知乎live笔记]如何得到好的科研Idea
    POJ 2787:算24
    POJ 2964:日历问题 日期转换+闰年月份可放在一个month[2][12]数组里
    POJ-1835 宇航员 空间方向模拟+打表
  • 原文地址:https://www.cnblogs.com/pmer/p/3181570.html
Copyright © 2011-2022 走看看