代码写得要"拽"(DRY)——《C解毒》试读

    DRY:Don’t repeat yourself,是编写出优质代码的一个重要原则。其含义就是不要四处写同样的代码。简言之,干净利落,是为“拽”(DRY)。
    有些初学者喜欢把相同或类似功能的代码四处粘贴,就属于典型的self-repeat行为。这样编写代码的一个显著危害就是一旦你需要修改相应的代码,你就得疲于奔命四处奔波地去修改每一处代码。
    即便如此,你心里恐怕还难免惴惴不安,因为你很可能挂万漏一。即使你确信已经全面没有遗漏地进行了修改,你更没底的事情是每处都进行了正确的修改。因为按照概率分析,如果你有99%把握做一次正确修改,那么做十次正确修改不出错的可能性就会下降到90%。
    退一步说,你能保证四处修改都不失误,四处修改所需要花费的精力也是惊人的(这一点在程序规模很小的时候不明显,所以也很难被初学者所重视,正所谓“不吃一堑,不长一智”),你几乎不太可能还有精力关注编程过程中更重要的事情——比如改进算法等等。
    即使没有没有上述种种弊端,最终你的代码也一定臃肿不堪丑陋无比的。
    所以任何一个合格程序员都懂得把相同功能的代码段抽象为一个函数。在需要这些功能的地方只进行一次简单的调用。这就避免了前面提到的种种弊端。
    下面要谈到的是另一种很不“干爽”(DRY)即很“潮”的写法,这种写法“潮”在函数内多次重复相同的语句而显得笨拙。

int main( void )
{
int n;

printf(
"please input the value of n: ");
scanf(
"%d",&n);
while(n<=0)
{
printf(
"error!");
printf(
"please input the value of n: ");
scanf(
"%d",&n);
}

/*其他语句*/

}

   这段代码的功能显然是通过标准输入设备输入n的值,如果输入不是十进制正整数,则提示用户并要求用户重新输入。
   然而很明显,在这个代码段中 
   printf("please input the value of n: ");
   scanf("%d",&n);
这两句重复出现。
   对于有的人来说,对此熟视无睹麻木不仁没有任何感觉;对于另一些人来说,就如同看到作文中出现重复啰嗦的句子一样会感到别扭和不自然。这是一个境界的问题。而在后一种人当中有一部分知道如何修改,也有一部分人不清楚如何修改。本文就是为这部分有感觉但不清楚如何修改的人所做。治疗麻木不仁感觉丧失的药方我这里没有。
   前面的代码其实可以在不改变功能的前提下,改写为:

   while( printf("please input the value of n: ") ,
          scanf("%d",&n) , 
          n<=0 ) 
      printf("error!");

   这种写法没有任何多余之处,比前一种写法简洁许多。下面对这种写法的含义稍作解释。
   我们知道,C语言while语句的一般形式为
     while(表达式)
        语句
    这里的“表达式”只要是标量类型(scalar type)就可以。第二种写法中while关键字后()内的表达式是一个逗号表达式,其值的类型为子表达式“n<=0”的类型,而“n<=0”的类型无疑属于标量类型,因而第二种写法满足C语言的语法要求。
    这个逗号表达式的求值过程是:(1)求printf("please input the value of n: ")这个函数调用表达式的值,然而在这里我们并不需要也不使用这个值。在求值过程中会伴随有相应的副效应(side effect)——在标准输出设备上输出“please input the valueof n: ”这一字符序列,这才是我们所需要的。(2)求函数调用表达式scanf("%d",&n)的值,这个值我们同样不需要。我们所需要的依然是函数调用表达式scanf("%d",&n)的副效应,其中最重要的副效应是,变量n得到了一个新的值。(3)紧接着,由于变量n通过输入有了有意义的值,就可以求“n<=0”这个子表达式的值了,这个值是执行while语句所需要的。这个值也是第二种写法和第一种写法等价的必要条件之一。另一个必要条件是printf("please input the value of n: "),scanf("%d",&n)及printf("error!")这几个函数调用副效应出现的次序相同,由于逗号表达式运算规则对求值次序的规定,这种次序同样得到了保证。
    其他表现力不强的语言(比如BASIC)可能必须使用第一种方法写代码,那是由于语言自身的局限,是没有办法的事。但是对于C语言来说,完成同样的功能却完全可以写的更加潇洒飘逸。这主要归功于C语言强大功能的表达式。
    由于C语言表达式功能的强大,对于更高的功能要求,这种写法同样奏效。比如,假如要求输入的不是正数或其他非法输入(如输入了非十进制字符)时,皆提示用户并要求用户重新输入,则代码可以写成:

    while( printf("please input the value of n: ") ,
          scanf("%d",&n)==0 || n<=0 ) 
    {
     //一些必要的处理
    }

    当输入不满足%d格式要求时,scanf()调用结束且返回值为0。这里,“,”和“&&”共同保证了必要的顺序。
    
    至于,前面第一种、第二种写法究竟哪一个好,这个问题还是留给您自己吧。凡是价值判断可能都免不了要带有一些主观色彩。更何况,根据我的经验,一旦你提出吃饭应该用饭碗的时候,必定会有很多用痰盂吃饭的冲上前来,和你激辩用痰盂吃饭也是完全可以的。
    如果有人喜欢并坚持用痰盂吃饭,关我屁事!

posted @ 2011-07-16 22:12  garbageMan  阅读(3167)  评论(39编辑  收藏  举报