混乱是怎样炼成的——《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;
}
则非但不忙不乱,简直称得上是有些游手好闲了。