C中的惰性算术

时间:2013-10-06 07:37:00

标签: c lazy-evaluation

据我所知,C对逻辑表达式使用延迟计算,例如: G。在表达

f(x) && g(x)
如果g(x)为假,则不会调用

f(x)

算术表达式如

呢?
f(x)*g(x)

如果g(x)为零,是否会调用f(x)

5 个答案:

答案 0 :(得分:3)

是的,算术运算是急切的,而不是懒惰的。

所以在f(x)*g(x)中总是调用fg(迂腐地,编译器正在将其转换为某些A-normal form,如果不可观察,甚至可以避免一些调用),但无法保证在f之前或之后调用g的顺序。当x*1/x为0时,评估y*1/xxundefined behavior

在Haskell AFAIU中并非如此

答案 1 :(得分:2)

是的,g(x)仍然会被调用。

一般来说,仅仅因为左侧是零,有条件地忽略右侧的评估是非常缓慢的。也许不是在右侧是昂贵的函数调用的情况下,但编译器不会假设知道这一点。

答案 2 :(得分:2)

它叫做"Short Circuit"而不是懒惰。并且,至少就标准问题而言,是 - 即,它没有指定*的短路评估。

如果可以确定g()没有副作用,编译器可能能够进行短路评估,但只能在as-if规则下进行(即,它只能通过发现没有副作用来实现外部可观察到的差异,不是因为标准给予它任何直接许可这样做。)

答案 3 :(得分:1)

如果逻辑运算符&&||评估顺序从左到右发生,短路发生。 在评估&&(逻辑AND),||(逻辑OR)的左右操作数之间存在序列点(作为短路评估的一部分)。例如,在表达式*p++ != 0 && *q++ != 0中,子表达式*p++ != 0的所有副作用都在尝试访问q之前完成,但在算术运算符的情况下则不然。

答案 4 :(得分:1)

虽然可以进行优化,但有一些反对意见:

  • 您可能会为优化支付的费用高于您从中获得的优惠:与逻辑运算符不同,优化可能只对所有具有算术运算符的情况的一小部分有益,但同时需要为每个操作额外检查0。

    因为布尔值真值只有两个可能的值,所以第二个操作数不需要的短路布尔表达式有theoretical 50%几率(1÷2)被评估。 (这假设均匀分布,这可能不太现实,但请耐心等待。)也就是说,您可能会在相对较大比例的情况下从优化中获利。

    将此与整数进行对比,其中0只是数百万中可能值中的一个。第一个操作数为0的概率要低得多:1÷2 32 (对于32位整数,再次假设均匀分布)。即使0实际上发生的可能性稍大(即分布不均匀),我们仍然不可能处理与真值相同的数量级。

    浮点数学进一步加剧了这个问题。在这里,您需要处理舍入错误和非规范化的可能性。某些计算产生正好 0的概率可能甚至低于整数。

    因此,优化相对不太可能导致剩余的操作数未被评估。但导致100%的额外检查为零!

  • 如果您希望评估规则保持合理一致,则必须重新定义&&||的短路评估顺序:部门有一个重要的极区情况,即除以0:即使第一个操作数为0,商也不一定为0。除以0之外的部分将被视为错误(可能在IEEE浮点数学中除外);因此,您始终必须评估第二个操作数,以确定计算是否有效。

    /有一个替代优化:除以1.在这种情况下,你根本不需要除法,而只是返回第一个操作数。因此,从第二个操作数(除数)开始,可以更好地优化/

    现在,除非您希望&&||*开始使用第一个操作数进行评估,但/要从第二个操作数开始(这可能看起来不直观) ),你必须通常重新定义短路行为,以便首先评估第二个操作数总是,这将偏离现状。

    这本身不是问题,但如果C语言发生了变化,可能会破坏很多现有代码。

  • 优化可能会破坏与C ++代码的“兼容性”,其中运算符可能会过载。优化是否仍适用于重载的*/运算符?或者是否必须有两种不同形式的这些操作符,一种是短路的,另一种是急切的评估?

    同样,这不是短路算术运算符固有的缺陷,但如果将这种短路作为一种重大改变引入C(和C ++)语言,就会出现这个问题。