为什么语言不能使用短路评估?

时间:2009-09-18 17:28:04

标签: language-agnostic conditional short-circuiting

为什么语言不能使用Short-circuit evaluation?不使用它有什么好处吗?

我看到它可能导致一些表演问题......这是真的吗?为什么呢?


相关问题:Benefits of using short-circuit evaluation

18 个答案:

答案 0 :(得分:40)

不使用短路评估的原因:

  1. 因为如果你的函数,属性获取或操作符方法有副作用,它会表现不同并产生不同的结果。这可能与以下内容相冲突:A)语言标准,B)您的语言的先前版本,或C)典型用户的语言的默认假设。这就是VB不会短路的原因。

  2. 因为您可能希望编译器可以自由地重新排序和修剪表达式,运算符和子表达式,而不是用户输入的顺序。这些是SQL的原因有没有短路(或者至少不是大多数开发人员认为它会这样做的方式)。因此,SQL(以及其他一些语言)可能短路,但只有当它决定并且不一定按照您隐式指定的顺序时。

  3. 我假设您在询问“自动,隐式特定于订单的短路”,这是大多数开发人员对C,C ++,C#,Java等的期望.BB和SQL都有明确的方法< em>强制订单特定的短路。然而,通常当人们提出这个问题时,这是一个“做我想要的”问题;也就是说,它们的意思是“为什么它不能做我想要的?”,就像我按照我写的顺序自动短路一样。

答案 1 :(得分:7)

我能想到的一个好处是,某些操作可能会产生您可能会发生的副作用。

示例:

if (true || someBooleanFunctionWithSideEffect()) {
    ...
}

但这通常不赞成。

答案 2 :(得分:5)

默认情况下,Ada不会这样做。为了强制进行短路评估,您必须使用and thenor else代替andor

问题在于,在某些情况下,它实际上会减慢速度。如果第二个条件快速计算,并且第一个条件对于“和”几乎总是为真,或者对于“或”则为假,那么额外的检查分支指令就是一种浪费。但是,据我所知,对于具有分支预测器的现代处理器,情况并非如此。另一个问题是编译器可能碰巧知道第二个一半更便宜或可能失败,并且可能想要相应地重新排序检查(如果定义了短路行为,则无法做到这一点) )。

我听说过,在第二次测试有副作用的情况下,它会导致代码出现意外行为。恕我直言,如果你不太了解你的语言,那只是“出乎意料”,但有些人会说这个。

如果您对实际语言设计师对此问题的看法感兴趣,请参阅Ada 83 (original language) Rationale的摘录:

  

布尔表达式的操作数   例如A和B可以评估   任何订单。取决于复杂性   在术语B中,它可能更多   有效(在一些但不是全部   机器)只在评估B时   术语A的值为TRUE。这个   然而,这是一个优化决定   由编译器采取,它将是   不正确地假设这个   始终进行优化。其他   我们可能想要表达的情况   每个条件的结合   条件应该评估(有   意思)只有前一个   条件得到满足。这两个   事情可以通过短路来完成   控制表......

     

在Algol 60中,人们可以达到效果   短路评估只能通过   使用条件表达式,因为   完成评估   除此以外。这常常导致   那些乏味的结构......

     

有几种语言没有定义   布尔条件是   评估。因此计划   基于短路评估会   不便携。这清楚了   说明需要分开   来自短路的布尔运算符   控制表格。

答案 3 :(得分:4)

查看我在On SQL Server boolean operator short-circuit的示例,其中显示了如果使用布尔短路 ,SQL中的某个访问路径会更有效。我的博客示例显示了如果您假设SQL中存在短路,实际依赖布尔短路会如何破坏您的代码,但如果您阅读了为什么是SQL首先评估右侧的原因,那么我会看到这是正确的,这会带来更好的访问路径。

答案 4 :(得分:4)

Bill提到了一个不使用短路的正当理由,但更详细地说明了这一点:高度并行的架构有时会出现分支控制路径问题。

以NVIDIA的CUDA架构为例。图形芯片使用SIMT架构,这意味着在许多并行线程上执行相同的代码。但是,如果所有线程每次都采用相同的条件分支,则此有效。如果不同的线程使用不同的代码路径,则评估被序列化 - 这意味着并行化的优势会丢失,因为某些线程必须等待,而其他线程则执行备用代码分支。

短路实际上涉及分支代码,因此短路操作可能对CUDA等SIMT架构有害。

- 但就像比尔说的那样,这是一个硬件考虑因素。至于 languages ,我会用响亮的 no 回答你的问题:防止 的短路。

答案 5 :(得分:4)

我会说100次中有99次我更喜欢短路算子的性能。

但是我找到了两个我不会使用它们的重要原因。 (顺便说一句,我的例子是在C中,&amp;&amp;和||是短路的,&amp;和|不是。)

1。)如果要在if语句中调用两个或更多函数,而不管第一个函数返回的值。

if (isABC() || isXYZ()) // short-circuiting logical operator
    //do stuff;

在这种情况下,仅当isABC()返回false时才调用isXYZ()。但是你可能想要无论如何都要调用isXYZ()。

所以你要这样做:

if (isABC() | isXYZ()) // non-short-circuiting bitwise operator
    //do stuff;

2。)当您使用整数执行布尔数学时。

myNumber = i && 8; // short-circuiting logical operator

不一定与:

相同
myNumber = i & 8; // non-short-circuiting bitwise operator

在这种情况下,您实际上可以获得不同的结果,因为短路运算符不一定会评估整个表达式。这使得它对布尔数学基本没用。所以在这种情况下,我会使用非短路(按位)运算符。

就像我暗示的那样,这两种情况对我来说真的很少见。但是你可以看到两种类型的运营商都有真正的编程原因。幸运的是,今天大多数流行语言都有。甚至VB.NET也有AndAlso和OrElse短路运算符。如果今天的语言不具备这两种语言,我会说它落后于时代,并且真正限制了程序员。

答案 6 :(得分:3)

如果您想要评估右侧:

if( x < 13 | ++y > 10 )
    printf("do something\n");

也许你希望y增加,无论x&lt; 13.然而,反对这样做的一个好理由是创造没有副作用的条件通常是更好的编程实践。

答案 7 :(得分:3)

作为一个延伸:

如果你想要一种语言超级安全(以令人敬畏的代价),你会删除短路评估。当某些“安全”需要花费不同的时间时,可能会使用Timing Attack来捣乱它。短路评估会导致执行不同时间的事情,从而为攻击挖掘漏洞。在这种情况下,甚至不允许短路评估有望帮助编写更安全的算法(无论如何都是定时攻击)。

答案 8 :(得分:2)

Ada编程语言支持两个没有短路的布尔运算符(ANDOR),以允许编译器优化并可能并行化构造,并且运算符具有显式请求短路(AND THENOR ELSE)这是程序员所希望的。这种双管齐下的方法的缺点是使语言变得更复杂(1000个设计决策采用相同的“让我们两个都做!”静脉将使编程语言总体上更复杂; - )。

答案 9 :(得分:2)

The language Lustre不使用短路评估。在if-then-elses中,then和else分支在每个tick处被评估,并且一个被认为是条件的结果,取决于对条件的评估。

原因是这种语言和其他同步数据流语言具有简洁的语法来说明过去。需要计算每个分支,以便在将来的周期中有必要时可以使用每个分支的过去。该语言应该是有用的,所以这并不重要,但你可以从中调用C函数(并且可能注意到它们的调用次数比你想象的要多)。

在Lustre中,写出相当于

的内容

if (y <> 0) then 100/y else 100

是一个典型的初学者错误。不能避免除零,因为甚至在y = 0时的循环中也会计算表达式100 / y。

答案 10 :(得分:2)

并不是说我认为这就是现在任何语言都在发生的事情,但将操作的两个方面都提供给不同的线程会非常有趣。大多数操作数可以预先确定不会相互干扰,因此它们可以作为传递到不同CPU的良好候选者。

这种事物的关键在于高度并行的CPU,它们倾向于评估多个分支并选择一个分支。

嘿,这有点长,但你问“为什么会用语言”......而不是“为什么要用语言”。

答案 11 :(得分:1)

因为短路可能会改变应用程序IE的行为:

if(!SomeMethodThatChangesState() || !SomeOtherMethodThatChangesState())

答案 12 :(得分:1)

我认为它对可读性问题有效;如果有人以不完全明显的方式利用短路评估,维护人员很难看到相同的代码并理解逻辑。

如果内存服务,erlang提供两个构造,标准和/或,然后和/或/。这澄清了'是的,我知道这是短路的,你也应该',在其他方面需要从代码中获得意图。

作为一个例子,假设维护者遇到了这些问题:

if(user.inDatabase() || user.insertInDatabase()) 
    user.DoCoolStuff();

需要几秒时间才能识别出意图是“如果用户不在数据库中,请插入他/她/它;如果有效则可以做很酷的事情”。

正如其他人所指出的那样,这只是在做副作用时才有意义。

答案 13 :(得分:0)

关于副作用问题已有很好的回应,但我没有看到有关该问题的表现方面的任何内容。

如果您不允许进行短路评估,则性能问题是必须对双方进行评估,即使它不会改变结果。这通常不是问题,但在以下两种情况之一下可能会变得相关:

  • 代码位于一个非常频繁的内循环中
  • 与评估表达式相关的成本很高(可能是IO或昂贵的计算)

答案 14 :(得分:0)

许多答案都谈到了副作用。这是一个没有副作用的Python示例,其中(在我看来)短路提高了可读性。

for i in range(len(myarray)):
  if myarray[i]>5 or (i>0 and myarray[i-1]>5):
    print "At index",i,"either arr[i] or arr[i-1] is big"

短路确保我们不会尝试访问myarray [-1],这会引发异常,因为Python数组从0开始。代码当然可以在没有短路的情况下编写,例如。

for i in range(len(myarray)):
  if myarray[i]<=5: continue
  if i==0: continue
  if myarray[i-1]<=5: continue
  print "At index",i,...

但我认为短路版本更具可读性。

答案 15 :(得分:0)

VB6不使用短路评估,我不知道是否有新版本,但我对此表示怀疑。我相信这只是因为旧版本也没有,因为大多数使用VB6的人都不会发生这种情况,这会导致混淆。

这只是让我变得非常难以成为一名编写意大利面条代码的noob VB程序员,并继续成为真正程序员的旅程的事情之一。

答案 16 :(得分:0)

短路评估自动提供表达式的一部分的条件评估。

主要优点是它简化了表达。

性能可以提高,但你也可以观察非常简单的表达式的惩罚。

另一个结果是评估表达的副作用可能会受到影响。

一般而言,依赖副作用并不是一种好习惯,但在某些特定情况下,它可能是首选解决方案。

答案 17 :(得分:0)

我不知道任何性能问题,但一个可能的论证是避免它(或至少过度使用它)是可能混淆其他开发人员。