混乱是怎样炼成的——《C解毒》试读

4.有一个已排好序的数组,要求输入一个数后,按原来排序的规律将它插入数组中。

#include <stdio.h>
int main( )
{ int a[11]={1,4,6,9,13,15,19,18,40,100};
  int temp1,temp2,number,end,i,j;
  printf("array a:\n");
  for(i=0;i<10;i++)
    printf("%5d",a[ i ]);
  printf("\n");
  printf("insert data:");
  scanf("%d",&number);
  end=a[9];
  if(number>end)
    a[10]=number;
  else
     {for(i=0;i<10;i++)
       {if(a[ i ]>number)
          {temp1=a[ i ];
           a[ i ]=number; 
           for(j=i+1;j<11;j++)
              {temp2=a[j];
               a[j]=temp1;
               temp1=temp2;
              }
          break;    
          }
       }
     }     
  printf("Now array a:\n");
  for(i=0;i<11;i++)
    printf("%5d",a[ i ]);
  printf("\n");
  return 0;
}

——谭浩强 ,《C程序设计(第四版)学习辅导》,清华大学出版社,2010年7月,p57~58


这个题目很简单,但代码却混乱无比,堪称混乱之集大成者。如果想学习怎样把代码写的混乱无比,从中可以品悟出很多独家秘诀。

秘诀一、无聊

int end ;
  end=a[9];
  if(number>end)
//……

这段代码堪称无聊之代表作。方法很简单,就是定义一个并不需要的变量(end),毫无意义地对它赋值(end=a[9]),再用这个克隆的变量(end)做“>”运算而不是用原来的数据对象(a[9])进行运算。而使用原来的数据对象做运算,除了可以提高程序效率、使得代码可读性更好之外,实在是一点也不够无聊啊。不无聊的代码写起来很容易很简单,通常酱紫地:

  if ( number > a[9] )
//……

秘诀二、忽左忽右,前后不一

把一个数插入到一个有序的数组中,通常有两种考虑:或者从前往后先找到待插入数的位置再做打算,或者从后向前逐步调整。但这绝对达不到混乱的目的。样本代码中先是把数据放在了最后

 

    a[10]=number;

貌似要从后向前,然而这只是一个虚招。紧接着又

for(i=0;i<10;i++)
       {if(a[ i ]>number)

这又是前向后的招数了。

这里,制造混乱的要诀在于忽前忽后,忽左忽右,任何一个招数都不使老,任何一个思路都不贯彻始终,这样才能让人捉摸不透,制造混乱,之后方可在乱中取胜。
而寻常的招数则是无条件地先把待插入的数放在数组的最后,例如

scanf("%d",&a[10]);

然后再对数组元素的次序逐步调整以到达有序的状态。

或者,先从前面找到待插入数据的正确位置,例如

int insert_pos ;
scanf("%d",&number);
for(insert_pos=0; insert_pos < 10 && number > a[insert_pos]; insert_pos++)
    ;

待找到应该插入的位置之后再做进一步的处理。
寻常的招数固然思路清晰且简洁易懂,然而主要的缺点就是达不到天下大乱之奇功。

秘诀三、颠三倒四,手忙脚乱

如果按照从前向后的思路,在找到待插入的位置后,首先考虑的应该是把这个位置上和以后位置上的数组元素向后移动,空出待插入元素的位置,然后插入:

for( i = 11 – 1 ; i > insert_pos ; i--)
  a[i]=a[i-1];
a[insert_pos]= number;

之后数组成为新的有序状态。然而,这种简单的做法实在是属于乱而无术。究竟怎样才能达到乱而有术的境界呢?样本代码给我们做出了极出色的示范。
首先,在找到待插入的位置后,并不等安排好其他数组元素的位置再插入,而是迫不及待,把数据插入进去

     {for(i=0;i<10;i++)
       {if(a[ i ]>number)
          {temp1=a[ i ];
           a[ i ]=number; 

这样,temp1这样多余的变量就必不可少,总不能把被排挤出的原来的a[i]给摔掉啊。紧接着

           for(j=i+1;j<11;j++)
              {temp2=a[j];
               a[j]=temp1;
               temp1=temp2;
              }

就如同马戏团小丑表演的抛球杂耍一样——不停地把球抛起(temp2=a[j];)、接住(a[j]=temp1;),同时忙里抽闲地还要不停地从一只手倒入另一只手中(temp1=temp2;),颠三倒四手忙脚乱得让人眼花缭乱目不暇接。这种把几件事搅和在一起忙活的方法实乃制造混乱秘笈中之秘笈。

忙完之后,发现寻找插入位置的循环竟然还没结束,于是,赶紧刹车——

          break;    

仿佛马戏表演进入高潮之后戛然而止。幸好制动及时,没有照成追尾。后面再像没事人一样按平常的方式输出——大乱之后的大治。实在是乱出了风格乱出了水平,乱的让人高山仰止。相比之下,下面这种写法

#include <stdio.h>

int main( )
{
int a[11] = {1,4,6,9,13,15,19,18,40,100};
int number , i , insert_pos ;

printf(
"array a:\n");
for( i =0 ; i <11-1 ;i++)
printf(
"%d ",a[i]);
putchar(
'\n');

printf(
"insert data:");
scanf(
"%d",&number);

//寻找正确的插入位置
for( insert_pos =0 ;
( insert_pos
<11-1 ) && (number > a[insert_pos]) ;
insert_pos
++ )
;

//插入位置及后面的元素后移
for( i =11-1 ; i > insert_pos ; i-- )
a[i]
=a[i-1];

//插入
a[insert_pos]= number;

//输出
printf("Now array a:\n");
for( i =0 ; i <11 ; i++ )
printf(
"%d " , a[i] );
putchar(
'\n');

return0;
}

就显得太过于井井有条昆乱不挡了。貌似一副怎么也乱不起来的样子。更有甚者

#include <stdio.h>

int main( )
{
int a[11] = {1,4,6,9,13,15,19,18,40,100};
int i ;

printf(
"array a:\n");
for( i =0 ; i <11-1 ;i++)
printf(
"%d ",a[i]);
putchar(
'\n');

printf(
"insert data:");
scanf(
"%d",&a[ 11-1 ] );

for( i =11-1 ; (i >0) && ( a[i] < a[i-1] ) ; i-- )
{
int temp ;
temp
= a[ i ] ;
a[ i ]
= a[ i -1] ;
a[ i
-1 ] = temp ;
}

//输出
printf("Now array a:\n");
for( i =0 ; i <11 ; i++ )
printf(
"%d " , a[i] );
putchar(
'\n');

return0;
}

则非但不忙不乱,简直称得上是有些游手好闲了。

posted @ 2011-08-07 15:12  garbageMan  阅读(1868)  评论(31编辑  收藏  举报