为什么大多数编程语言都会对传递给函数的参数使用急切的评估?

时间:2012-01-10 02:49:13

标签: language-agnostic operator-precedence

在大多数编程语言中,传递给函数的参数在函数使用它们之前进行评估,也就是说,它们会被急切地评估。

对我来说,似乎只有在函数使用它们时才能更有意义地评估参数,即懒惰。这对我来说更有意义,因为它似乎会带来性能上的好处:为什么要评估甚至不需要的东西?

此外,假设你想实现一个接受布尔值的if函数,如果布尔值为真则返回一个返回的对象,如果布尔值为假则返回另一个返回的对象:

object if(bool condition, object valueIfTrue, object valueIfFalse) {
  if(condition) return valueIfTrue;
  return valueIfFalse;
}

在一种急切评估参数的语言中,两个对象总是被评估,即使该函数总是只需要其中一个,这最多会产生轻微的不必要的开销,并且最坏的情况会导致无限循环。 / p>

也就是说,由于大多数编程语言都使用对函数参数的急切评估,我认为必须有一个理由为什么它通常以这种方式完成。在这里我是否会忽视热切评估的一些重大好处,是因为它更容易以这种方式实现语言,它只是传统还是什么?

5 个答案:

答案 0 :(得分:5)

有几个原因我看到了热切的评价,这两个原因都非常重要:

  1. 急切的评估意味着副作用会立即发生。如果你使用懒惰评估,你不能依赖你以前做过的事情的副作用才能生效。
  2. 懒惰的评估会带来一定的内存膨胀。存储计算结果通常比存储描述计算的thunk所需的内存少得多。这可能导致使用太多内存(即时间与内存权衡),有时更重要的是,更难以确定程序/算法的内存特性。
  3. 懒惰的评估可以是一个强大的工具,但它不是没有它的成本。纯功能语言倾向于避免问题#1,因为它们没有副作用(一般情况下),但有时仍被问题#2咬伤。允许延迟评估的语言(LISP宏是这种形式,虽然与懒惰评估不同)可以拥有两全其美的优势,但代价是程序员需要付出更多努力。

答案 1 :(得分:3)

选项1 - 将所有参数加载到寄存器中,调用函数

选项2 - 加载第一个参数,评估是否有必要,等待CPU管道清除,获取下一个参数,评估是否有必要......然后将所需的参数加载到寄存器中,执行带有额外逻辑的函数来标记哪些寄存器正在使用中。

在等待查看正在执行的代码路径(通过分支预测稍微保存)时,“if”已经会导致性能持续性

答案 2 :(得分:3)

为了使懒惰评估工作,需要在某处有额外的代码和数据来跟踪表达式是否已被评估。在某些情况下,这比急切评估更昂贵。确定表达式是否可以从惰性求值中获益可能需要对程序如何工作有非常高级的了解;编译器和/或解释器肯定不会有这种知识。

此外,如果函数或表达式具有副作用,则延迟评估策略可能会使程序以违反直觉且难以调试的方式运行。在功能编程语言中,这当然不是问题,因为设计中没有副作用。实际上,懒惰评估是大多数(如果不是全部)函数式编程语言的默认策略。

话虽如此,没有什么可以阻止你在不同的地方使用这两种策略。如果在非平凡的程序中使用混合方法,我不会感到惊讶。

答案 3 :(得分:2)

除了已经提供的优秀答案之外,懒惰评估还存在另一个实际问题。如果你有一系列表达式只是在最后一个被“使用”时被懒惰地评估,那么很难找出性能瓶颈。

答案 4 :(得分:0)

回到白垩纪时期,有很多语言可以做到这一点。例如,SNOBOL。 ALGOL 68有一个“按名称呼叫”功能,它做了类似的事情。 C(以及它的许多衍生物)在一个非常特殊的情况下完成它,它描述为“短路”布尔表达式。一般来说,它几乎总是比实现权力更多的混乱和错误的来源。