我正在编写一个 2D 曲线算法,并且有一些代码可以有效地进行求和:
for (i=0, end=...; i<end; i++) {
value += coefficients[i] * expensiveToCalculateValue(i);
}
其中coefficients[i]
某些迭代步骤的值为零。由于零倍的东西仍然是零(至少在简单的算术规则下),我想我可以通过首先检查是否coefficients[i]
为零来显着优化这段代码,如果是,就continue
到下一次迭代。添加,排序,工作出色。
但这留下了一个问题:为什么不为我这样做?这不是乘法的一些创造性的利基版本,它是简单的算术。几乎所有语言都会短路二进制 OR 和 AND 运算,如果找到一个操作数会使结果从那时起保持不变,那么为什么算术乘以零不会同样短路呢?
我尝试在 Java、PHP、JavaScript、Perl、Python、C++ 中运行这段代码(修改为 synax),甚至看看 Prolog 做了什么,但他们都没有意识到,一旦他们看到“零次......”他们不必评估可能昂贵的第二(或第三、第四等)术语:
printed = 0;
function veryExpensive() {
print "oh god this costs so much, x" + (printed++);
return 0;
}
value = 0 * veryExpensive() * veryExpensive() * veryExpensive()
所有这些最终都运行veryExpensive()
了三遍。
现在,我知道你可以- 如果你是那种人 - 编写你的veryExpensive
函数来执行管理开销工作,基于这样一个事实,你可以依赖它被执行,尽管它的结果对算术表达式没有贡献(如果你这样做那,你可能在滥用一种语言,但每个人在他们的编程生涯中的某个时候都喜欢偷偷摸摸的忍者代码),但你这样做只是因为你知道这种语言碰巧碰巧没有针对这种情况进行优化。如果该语言为您优化了算术评估,那么您的代码表达能力就不会完全受到阻碍。
那么:是否有一些历史先例导致大量当前使用的语言针对“true OR ...”和“false AND ...”而不是“zero TIMES ...”进行优化?为什么我们要优化二元运算,而不是 MUL 0?(如果我们幸运的话,有人有一个迷人的故事来讲述为什么我们现在不短路)
更新
John Skeet 和 Nik Bougalis 都给出了很好的论据,说明为什么用现存的语言优化它会导致问题,但 Nik 的答案与问题更吻合,所以我将他的答案标记为“正确”的答案。也就是说,它们涵盖了同一问题的不同方面,因此真正的答案是两者的结合。