回避复杂—《狂人C》习题解答(第3章题7)

题目:
      5个小孩围成一圈分糖果。最初第一个小孩有66块糖果,第二个有35块,第三个有24块,第四个有7块,第五个有34块。第一个小孩把自己的一半分给第二个小孩,然后第二个小孩把自己现有的一半分给第三个小孩……最后第五个小孩把自己现有的一半分给第一个小孩。编程模拟这个过程,输出最后每个小孩的糖果数。(每个孩子的糖果数始终为整数)

      由于在计算过程中需要记住每个小孩的糖果数目,所以使用变量存储。这些变量都有初值,因此可在定义这些变量时直接给这些变量赋初值。

#include <stdio.h>
#include <stdlib.h>

#define PORTION 2

int main( void )
{
  int num_1 = 66 ,
      num_2 = 35 ,
      num_3 = 24 ,
      num_4 =  7 ,
      num_5 = 34 ;

  system("PAUSE"); 
  return 0;
}

    然后逐步模拟分配的过程。首先是“第一个小孩把自己的一半分给第二个小孩”

#include <stdio.h>
#include <stdlib.h>

#define PORTION 2

int main( void )
{
  int num_1 = 66 ,
      num_2 = 35 ,
      num_3 = 24 ,
      num_4 =  7 ,
      num_5 = 34 ;

  //第一个小孩把自己的一半分给第二个小孩
  num_2 += num_1 / PORTION ;
  num_1 /= PORTION ; 

  system("PAUSE"); 
  return 0;
}

      如果写出这样的代码,逻辑上是有问题的。因为第二条语句本应该写成“num_1 -= num_1 / PORTION ;”(num_1 - num_1 / 2 =>  num_1 ),尽管其结果与“num_1 /= PORTION;”( num_1 / 2 =>  num_1 )相同,但“num_1 /= PORTION”实际上是编程者代替程序对“num_1 -= num_1 / PORTION”(num_1 - num_1 / 2 =>  num_1 )进行了进一步的演算和简化得到的。一般情况下,编程者应该把计算交给程序而不应该越俎代庖。如果题目改成了“第一个小孩把自己的三分之一分给第二个小孩”就会发现“num_1 -= num_1 / PORTION”(num_1 - num_1 / 3 =>  num_1 )依然正确,而“num_1 /= PORTION”( num_1 / 3 =>  num_1 )则是错误的。
      但是

  num_2 += num_1 / PORTION ;
  num_1 -= num_1 / PORTION ; 

      对次序有严格的要求,必须先执行第一句,再执行第二句。这就要求编程者在编程时付出更多的思考和谨慎,这也意味着出错的可能性更大些。如果一个写法比另一个写法更不容易出错,在两者都正确的前提下,显然第一种写法更好。学习编程只学习把代码写正确是远远不够的,更高的境界是学习写简单轻松的代码,不容易出错的代码。一定要学会回避复杂。把代码写得复杂以至于没有明显的错误固然不太容易,但编程更高的境界是,把代码写得非常简单以至于明显没有错误。
      在本题目中,这种简单轻松和不易出错体应现在:摆脱或减少对次序的强依赖,避免复杂的表达式。这可以通过增加一个临时变量实现。

{
  int num_to_send ;
  num_to_send = num_1 / PORTION ;
  num_1 -= num_to_send ; 
  num_2 += num_to_send ;
}

    这个写法后面两条语句不存在对次序的依赖,避免了“num_2 += num_1 / PORTION”这样的复杂的表达式,更直接、更简洁,因而是更好的写法。
    临时变量,有时也叫中间变量,通常用于存储计算过程中的中间计算结果。恰当地使用中间变量可以达到简化表达式,提高可读性的效果。
    下面是该问题解答的完整代码。

#include <stdio.h>
#include <stdlib.h>

#define PORTION 2

int main( void )
{
  int num_1 = 66 ,
      num_2 = 35 ,
      num_3 = 24 ,
      num_4 =  7 ,
      num_5 = 34 ;
      
  //第一个小孩把自己的一半分给第二个小孩
   {
     int num_to_send ;
     num_to_send = num_1 / PORTION ;
     num_1 -= num_to_send ; 
     num_2 += num_to_send ;
   }
   
  //第二个小孩把自己现有的一半分给第三个小孩
   {
     int num_to_send ;
     num_to_send = num_2 / PORTION ;
     num_2 -= num_to_send ; 
     num_3 += num_to_send ;
   }
      
  //第三个小孩把自己现有的一半分给第四个小孩
   {
     int num_to_send ;
     num_to_send = num_3 / PORTION ;
     num_3 -= num_to_send ; 
     num_4 += num_to_send ;
   }

  //第四个小孩把自己现有的一半分给第五个小孩
   {
     int num_to_send ;
     num_to_send = num_4 / PORTION ;
     num_4 -= num_to_send ; 
     num_5 += num_to_send ;
   }

  //第五个小孩把自己现有的一半分给第一个小孩
   {
     int num_to_send ;
     num_to_send = num_5 / PORTION ;
     num_5 -= num_to_send ; 
     num_1 += num_to_send ;
   }
  
  printf("最后每个小孩的糖果数分别为"
         "%d,%d,%d,%d,%d\n" , 
          num_1,num_2,num_3,num_4,num_5 ) ;

  system("PAUSE"); 
  return 0;
}
posted @ 2011-10-11 10:57  键盘农夫  阅读(2036)  评论(6编辑  收藏  举报