梦断代码——三数取大
前几天入手一本书《梦断代码》,讲和代码有关的种种故事。借用书名,我也着手写一些自己和代码的故事。
今天下午碰到这样一个小功能——一个数组有三个数,返回值最小的下标。这样的一个功能,在我开始学习编程的时候就碰到过,找出三个数中最大的那个数。
几乎不用思考,我会顺手写下如下的代码
int max_3(int a, int b, int c) {
if(a > b) {
if(b > c) {
return c;
}
else {
return b;
}
}
else {
if(a > c) {
return c;
}
else {
return a;
}
}
}
以前的我写下这段代码之后,就会觉得大功告成,只追求逻辑上的正确。两年之后,看过一些DXUT的代码,那些微软的高手写if else 会采用如下的方式:
if(cond) {
return a;
}
else {
return b;
}
改写成
if(cond) {
return a;
}
return b;
从视觉上看代码从6行变成了4行,我为这一发现感到十分高兴,这样的技巧并不花哨,纠正了一直以来养成的一个习惯——滥用了else。这样的技巧在《代码大全》中也曾提到过,读到那段时,心里产生很强的共鸣。因为在我工作中,碰到很多人,都没有注意到这个小细节,导致代码中出现大量的else,读起来略有困难。
回头再说到那个max_3函数,使用这个技巧可以将代码改进成如下样子
int max_3(int a, int b, int c) {
if(a > b) {
if(b > c) {
return c;
}
return b;
}
if(a > c) {
return c;
}
return a;
}
代码从18行减少成12行,功能不变,这样很有助于我们在更少的干扰中分析其逻辑。整个过程中减少代码的技巧都是很朴实的,并非特殊的技巧。
凭自己几年前的功力,我也只能把代码写到上面那样了,因为自己没有意识去找更好的方法。意识很重要,没有改进的意识就不会去探索…
再过两年,看过很多计算机编程的书籍之后,我会对上面那段代码产生一些疑问,上面代码中return c出现了两次,return a和return b只出现了一次。逻辑上是没有错误的,但总觉得有些不爽。
感觉不对的心理源于两个方面:
1.a,b,c的概率是一样的,所以对应return的语句也应该一样
2.排比的格式对于代码的理解很有帮助
第一个原因是源于概率论,以及对所谓“代码之美”的挑剔。
第二个原因是源于自己的代码经验,代码格式上的类似一般来说意味着逻辑上的类似,我们在阅读此类代码时可以保持思维的延续性。
细细想来,代码中有两次return c的原因是因为a和b先做比较,他们之中的胜利者才和c做比较。有点像三个人比试武功,显然c活得更长一点。
而追求排比性,如果仅仅凭着一些改进代码的小技巧,是很难做到的。
我在纸上画着,直觉告诉我,似乎有更好的方法。
几分钟后,一个灵感跳出来……
如果有这样一个函数max是找到a,b之中较大的那个数,那么可以采用如下方案实现max_3
int max_3(int a, int b, int c) {
return max(max(a,b), c);
}
由此我们可以推出
比较a,b,c,d找到最大的
可以写成
return max(max(max(a, b), c), d);
恩……看到了吧,如果是更多的数,写这个函数也很简单——只要增加相应的max(… , x)即可,有一点排比的感觉,我觉得有点Lisp的味道了。
但实际上,我们不会真的写一个函数包含很多个参数,我们会用数组来存储那些数,整个问题就可以描述成:给一个含有n个元素的数组,找出其中最大的那个数。
当问题被这样描述的时候,我们又可以不费什么思考,就写出代码:
int max_v(int arr[], int n) {
int temp = arr[0];
for(int i = 0; i < n; i++) {
if(arr[i] > temp) {
temp = arr[i];
}
}
return temp;
}
写这个循环甚至比用if…else从3个数中找最大更简单,思维一直就被那个“3个数”给定死了,所以没有想到用循环来处理“3个数”的问题…
用循环处理这个问题,很自然会想到中间变量temp,回头用中间变量结合max(max(a, b), c)的思路,再来实现max_3这个函数时,问题就变简单很多了…
int max_3(int a, int b, int c) {
int temp = a;
if(b > a) {
temp = b;
}
if(c > temp) {
temp = c;
}
return temp;
}
当写出这段代码时,我有点释然了,用if…else处理“三数取大”问题的那种细微的不爽,换这种方式之后就没有了,两次if语句结构类似,理解起来简单多了。
再用小技巧让代码更具有排比性
int max_3(int a, int b, int c) {
int temp = a;
temp = b > temp ? b : temp;
temp = c > temp ? c : temp;
return temp;
}
6行代码,而且看起来更容易理解了。
比较最初的写法和现在的写法,差别很大,第一种写法只是单纯的穷举,而最后一种,包含了抽象过程。抽象思维是非常非常重要的,这也是为什么我去学习Lisp的原因。
自己的“梦断代码”第一篇算是到此结束,从中你能体会到一个Programmer对其Code的爱了吗?:)
受某个Lisp群一个题解法的影响,想到如下一个方法
int max_3(int a, int b, int c) {
return (a >= b && a >= c) ? a : max_3(b, c, a);
}
真是太极客了~
浙公网安备 33010602011771号