
2.2.2 短路计算
假设你需要为一个评论系统创建如程序清单2.5所示的守门人系统:当用户试图发送评论时,守门人会拒绝10秒内发送的后续评论(用户在发送垃圾消息),以及没有内容的评论(用户可能在输入内容前不小心点击了Comment按钮)。
守门人函数接受评论和用户ID作为实参。假设之前已经定义了一个secondsSince-LastComment()函数;该函数在给定用户ID后,查询数据库,返回该用户上次提交评论后经过的时间。
如果两个条件都满足,则把评论提交到数据库,否则返回false。
程序清单2.5 守门人

程序清单2.5是守门人的一种可行的实现。注意OR表达式,如果上次评论后经过的时间不到10秒,或者当前评论为空,那么该表达式将返回false。
实现相同逻辑的另外一种方法是调换两个操作数的顺序,如程序清单2.6所示。首先检查当前评论是否为空,然后检查上次提交评论的时间。
程序清单2.6 守门人的另外一种实现

这两个版本是否有优劣之分?它们定义了相同的检查,只不过检查次序不同。事实上,这两个版本是不同的。由于布尔表达式的计算方式,在收到某些输入时,这两个版本在运行时会表现出不同的行为。
大部分编译器和运行时会对布尔表达式进行所谓的“短路”优化。a AND b形式的表达式会被翻译为if a then b else false。这遵守AND的真值表:如果第一个操作数为false,则无论第二个操作数是什么,整个表达式都是false;如果第一个操作数为true,那么当第二个操作数也为true时,整个表达式才为true。
对a OR b会进行类似的翻译,使其成为if a then true else b。查看OR的真值表可知:如果第一个操作数为true,则无论第二个操作数是什么,整个表达式都是true;如果第一个操作数为false,则只有第二个操作数为true时,整个表达式才为true。
之所以进行这种翻译,是因为如果计算第一个操作数已经能够知道整个表达式的结果,则完全不必计算第二个操作数,而这也是其名称“短路”的由来。守门人函数必须执行两个检查:一个开销相对小的检查,用于确保收到的评论不为空,以及一个开销可能很大的检查,涉及查询评论数据库。在程序清单2.5中,先执行数据库查询。如果上次提交评论是在10秒之内,则短路甚至不会检查当前评论,而只是简单地返回false。在程序清单2.6中,如果当前评论为空,则不会查询数据库。通过先执行一个相对低开销的检查,第二个版本有可能会避免进行开销很大的检查。
布尔表达式计算的这个属性很重要,在组合条件时要记得运用:取决于左侧表达式的计算结果,短路操作可能不会计算右侧的表达式,所以应该首选按照开销最小到开销最大的顺序来排列条件。