可读代码的艺术(一):简化循环与逻辑

本文探讨了如何提高代码可读性,包括条件语句的编写技巧,如将变量放在左边,常量放在右边;if/else顺序的重要性;避免使用do/while循环;以及如何拆分和管理变量以增强可读性。建议在编程时优先处理正逻辑和简单情况,减少嵌套,并使用解释变量来提高代码的清晰度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Chapter7 把控制流变得易读
条件语句中参数的位置:
一般原则:将变量放在左边,常量放在右边。更宽泛地说,将比较稳定的变量放在右边,变化较大的放在左边。
如 if ( length >= 10) 而不是 if ( 10 <= length)
if ( bytes_received < bytes_expected)而不是if (bytes_expected > bytes_received)
稳定的值相当于标的(比较的目标,目标点),而在人一般的思维习惯中,倾向于拿着一个“变化”的值去跟一个“固定”的值比较。
但是,在非“大小”比较的情况下,上面的原则似乎不起作用,例如验证一个请求参数是否为某个特定值:
if ( request.getParameterValue("name")).equals("Brandon")) ...
此时将常量"Brandon"可以避免出现空指针的情况(上行的参数没有name或者值为空)

if/else顺序
一些参考原则:
* 首先处理正逻辑而不是负逻辑,如 if ( debug) 而不是 if ( !debug)
* 首先处理简单的情况。这个时候可以使if else出现在同一屏幕(同一视野范围)
* 首先处理感兴趣的部分或者可以的情况。
上述原则很可能会相互冲突,这个时候就需要靠自己权衡。一个可以参考的方法是看大脑首先关注的是那一部分,潜意识下会不自觉地关注的部分。根据直觉来做决定有时候也是很好的。如:
if ( !url.hasQueryParam("expand_all")) { ...} else{...}
看到这个语句时,expend_all会不自觉地闯入我们的脑海,占据主要位置,因此此时下面的写法可能更具可读性:
if (url.hasQueryParam("expand_all")) {...} else {...}

又比如,下面的情况先处理简单的负逻辑可能更好一些:
if  not file :
    # log the error
else:
    # do complex logic

?:条件表达式(三目运算符)
这个语法糖在一些情况下的确回事代码更简洁。下面的代码片段:
time_str += (hour >= 12) ? "pm" : "am"
这种方法不错,但我更倾向于下面的写法:
suffix =  (hour >= 12) ? "pm" : "am"
time_str += suffix
引入suffix,虽然增加了一个临时变量,但是它起到了一定的解释的作用。

下面的三元运算符看起来就不是那么明了了:
return exponent >= 0 ? mantissa * ( 1 << mentissa ) : matissa / ( 1 >> -exponent)
下面的写法可能更清楚:
if ( exponent >= 0 ) {
return mantissa * ( 1 << mentissa ) 
} else {
return  matissa / ( 1 >> -exponent)
}


避免使用do/while
do/while 形式的循环保证循环体至少被执行一次,但是从可读性解读来看,由于这种循环形式把循环判断条件放到了循环体的后面,不太符合正常的思维习惯。因此,不是万不得已,尽量不要使用这种方式。

从函数中提前返回:
一个函数中出现多个return语句的情况其实没有那么糟糕,甚至有时候是很受欢迎的。尤其是用“保护语句”(guard clause)来实现。

最小化嵌套:
多重嵌套的代码可读性很差,每一层嵌套都给读者的“思维栈”加上一个条件,理解起来很困难。多重嵌套往往是在原有的代码上不断做修改、增加功能而变得越来越复杂的,因此在嵌套结果中新增嵌套内容时,尤其要小心,应该站在全局的角度重新去思考代码结构。
* 通过提前返回减少嵌套:
在情况很复杂的时候,可以通过提前返回来减少嵌套深度。这个时候if/else的顺序组织就显得尤为重要,参考if/else 顺序的一些基本原则,同时可以使用”guard clause“来提前返回。精心组织代码,重构代码,往往会起到很好的效果。
* 减少循环内的嵌套:
循环结构内的嵌套经常伴随着break,continue的情况,循环,嵌套,break,continue综合在一起可能会使代码十分复杂,易读性很差。小心处理。

=============================
Chapter8 拆分超长的表达式
使用解释变量:
if line.split(':')[0].trip() == "root"
==>
username = line.split(':')[0].trip();
if username == "root"

使用总结变量:
引入用于足够信息量的变量可以使代码更易读。
if ( request.user.id == document.owner_id )
if ( request.user.id == document.owner_id ) ===>

final boolean user_owns_document =  request.user.id == document.owner_id ;
if ( user_owns_document) ...
这个总结变量的引入为读者提供了一个概念解释,提供了相应的背景知识,理解起来更容易。

使用德摩根定律:
在多个逻辑判断中,如果情况复杂,可以尝试使用德摩根定律来重新组织代码,看看是否更清晰。
1) not ( a or b or c)  <=> (not a) and (not b) and (not c) 
2) not ( a and b and c)  <=> (not a) or (not b) or (not c) 
总结起来: 分别取反, 转换与/ 或。 即分别取反,然后or转成and   and转成or

反向思维:
在情况复杂的逻辑判断中,考虑相反的情况也许会让我们豁然开朗,正如反证法在证明中的作用一样。

===让你的代码文档化====字面标称=====

=============================

Chapter9 变量与可读性
过多的变量或者变量的作用于太过广可能导致代码的可读性下降。当这个问题出现时,可以通过集中方式来改善:
1)减少变量。去除不必要的变量,引入解释变量或者总结变量可能会提高可读性,但是不必要的变量可能适得其反。
2)减小变量的作用域。 好好权衡作用域的范围大小。例如,有2个方法用到了同一个变量,第一个方法为变量复制,然后调用第二个方法,第二个方法使用这个变量进行逻辑处理。一种可能的方案如下:
private String nickName;
public void method1() {
    nickName = ...
    method2();
}
public void method2() {
    use nickName...
}

一种可能更具可读性的组织方式像下面这样:
public void method1(){
    String nickName = ...
    method2(nickName);
}

private void method2(String nickName){
    use nickName...
}

3)可能的话使用不变量或者常量(final,const,...)


---------------------------------
变量越多,就越难全部跟踪它们的动向。
变量的作用域越大,就需要跟踪它的动向越久。
变量改变得越频繁,就越来跟踪它的当前值。
(面向对象与函数式编程, Scala,LISP系的语言推崇的使用不变量)

------------------------------------

9.1 减少变量
1)减少没有价值的临时变量(解释变量,总结变量)
now = datetime.datetime.now()
root_message.last_view_time = now
这里的now变量没有起到多大作用,完全可以删除。变量的存在至少应该:
+ 拆分复杂的表达式
+ 做出更有意义的澄清和说明(如解释变量)
+ 减少冗余代码(多次使用)
上面的代码改成
root_message.last_view_time = datetime.datetime.now()
一样还是很明了,清晰。
2)减少中间结果
var remove_one = function(array , value_to_remove) {
    var index_to_remove = null;
    for (var i = 0; i < array.length; i +=1){
        if (array[i] === value_to_remove){
            index_to_remove = i;
            break;
         }
    }
    if ( index_to_remove !== null) {
        array.splice(index_to_remove,1);
    }
};

var remove_one = function(array , value_to_remove) {
    for (var i = 0; i < array.length; i +=1){
        if (array[i] === value_to_remove){
            array.splice(i,1)
            return;
         }
    }
};
很显然,第二种方式更为简洁可读,index_to_remove变量的引入没有起到太大作用,反而使代码可读性变差。
3)减少控制流变量
控制流的变量用于控制程序的流程,但是一般可以通过更好的代码组织结构来消除控制流变量。
但是需要注意,在多线程的控制中,控制流变量也许是很有用的。

9.2 缩小变量的作用域
--> 让你的代码对尽量少的代码可见。
在一定的代码范围内,尽量减少变量的个数,会使得代码更易读,逻辑更清晰,减少bug。通常,把大函数拆分成小函数或者把巨无霸的类拆分成小类的一个初衷就是数据分离,即变量分离。前文nickName的例子便是一个例子,把nickName降格为局部变量。通常,除非有足够的理由,应尽量保持变量的可见范围最小化。

嵌套作用域,在python和JS中,没有嵌套作用域的说法,即使嵌套结束,变量依然可见,注意与Java等其他语言区分。作用域在底层实现为程序的执行环节(参考《计算机程序的构造与解释》原版,python版本)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值