【发布时间】:2026-01-08 05:40:02
【问题描述】:
据我所知,急切求值/应用顺序会在应用函数之前对函数的所有参数求值,另一方面,惰性求值/正常顺序仅在需要时才求值。
那么,eager evaluation 和 applicative order 以及 lazy evaluation 和 normal order 这对术语有什么区别?
谢谢。
【问题讨论】:
标签: programming-languages evaluation sicp
据我所知,急切求值/应用顺序会在应用函数之前对函数的所有参数求值,另一方面,惰性求值/正常顺序仅在需要时才求值。
那么,eager evaluation 和 applicative order 以及 lazy evaluation 和 normal order 这对术语有什么区别?
谢谢。
【问题讨论】:
标签: programming-languages evaluation sicp
懒惰的评估最多评估一个术语一次,而正常顺序将评估它出现的频率。因此,例如,如果您有 f(x) = x+x 并将其称为 f(g(42)),那么 g(42) 在惰性求值或应用顺序下被调用一次,但在正常顺序下被调用两次。
渴望评估和应用顺序是同义词,至少在使用计算机程序的结构和解释中的应用顺序定义时,这似乎与您的匹配。 (Wikipedia 对应用顺序的定义略有不同,并将其作为急切求值的特例)。
【讨论】:
我也在读 SICP,我一直对作者给出的正常顺序的定义感到好奇。对我来说,这似乎与惰性评估很相似,所以我去寻找有关两者的更多信息。
我知道这个问题是很久以前提出的,但我查看了常见问题解答并发现没有提及回答旧问题,所以我想我会把我找到的内容留在这里,以便其他人可以在未来。
这是我发现的,我倾向于同意这些:
我会(和其他人一样)认为懒惰的评估和 NormalOrderEvaluation 是两个不同的东西;不同的是 上面提到的。在惰性求值中,参数的求值是 推迟到需要它,此时参数被评估 并将其结果保存(记忆)。论点的进一步使用 函数使用计算值。 C/C++ 运算符 ||、&& 和 ? : 都是惰性求值的例子。 (除非一些新手 C/C++ 程序员很笨,可以重载 && 或 ||,在这种情况下 重载版本按照严格的顺序进行评估;这就是为什么 && 和 ||运算符在 C++ 中永远不应该被重载)。
换句话说,每个参数最多被评估一次,可能不会 完全没有。
另一方面,NormalOrderEvaluation 重新计算表达式 每次使用它。想想 C 宏,CallByName 在语言中 支持它,以及循环控制结构的语义等。 正常顺序评估可能需要比应用顺序更长的时间 评估,并且可能导致副作用发生不止一次。 (这就是为什么,当然,带有副作用的陈述通常应该 不能作为 C/C++ 中宏的参数)
如果参数是不变的并且没有副作用,那么唯一的 两者的区别在于性能。的确,在纯粹 函数式语言,惰性求值可以看成是一种优化 正态评价。存在副作用或表达式 重新评估时可以返回不同的值,两者具有 不同的行为;正常顺序 eval 尤其有坏处 由于推理困难而在程序语言中享有盛誉 关于没有参照透明的此类程序
还应注意,严格顺序评估(以及惰性 评估)可以用支持正常顺序的语言来实现 通过显式备忘录进行评估。反之则不然。这个需要 传入可以调用/消息传递的 thunk、函数或对象 为了推迟/重复评估。
和
惰性求值结合了正序求值和共享:
• 除非必须(正常顺序),否则永远不要评估某事
• 切勿多次评估某事(分享)
【讨论】:
seq 运算符,它强制其左操作数在右操作数之前被评估。
来自Kevin Sookocheff 的Normal, Applicative and Lazy Evaluation 帖子(重点,风格改变了我的):
懒惰评估
虽然正常顺序评估可能会导致做额外的工作 要求对函数参数进行多次评估, 应用顺序评估可能会导致程序不 在它们的正常顺序等价物所在的地方终止。在实践中,大多数 函数式编程语言使用 lazy 解决了这个问题 评价。
通过惰性求值,我们可以延迟函数求值,从而避免 对同一函数的多次评估——因此结合了 正常顺序和应用顺序评估的好处。
懒惰 评估,我们在需要时评估一个值,然后 评估该表达式的所有副本都更新为新的 价值。实际上,传递给函数的参数存储在 内存中的单个位置,因此只需要评估参数 一次。也就是说,我们记住了我们确定的所有位置 参数将被使用,当我们评估一个函数时,我们替换 与价值的论据。
因此,惰性评估,每 参数最多评估一次。
这太长了,无法在问题下方发表评论,并且用它更新现有答案似乎不合适,因此这个答案。
【讨论】: