严格和懒惰不同?
我经常读到, 懒惰与非严格不一样,但我发现很难理解这种差异。 它们似乎可以互换使用,但我知道它们有不同的含义。 我希望有助于理解差异。
我有几个关于这篇文章的问题。 我将在这篇文章结尾总结这些问题。 我有几个示例片段,我没有测试它们,我只是将它们作为概念呈现。 我已经添加了引号以避免您查找它们。 也许它会在稍后提出同样的问题。
非严格防卫:
函数f被认为是严格的,如果应用于非终止表达式时,它也无法终止。 换句话说,如果f bot的值是|,f是严格的。 对于大多数编程语言,所有功能都是严格的。 但在Haskell中并非如此。 作为一个简单的例子,考虑const1,常量1函数,定义如下:
const1 x = 1
在Haskell中const1 bot的值是1.从操作上讲,由于const1不需要它的参数的值,所以它永远不会试图评估它,因此永远不会被捕获到一个非终结的计算。 出于这个原因,非严格函数也被称为“懒惰函数”,并被称为“懒惰地”或“按需”评估他们的论点。
- 对Haskell的一个简单介绍:函数
我非常喜欢这个定义。 这似乎是我能找到的理解严格的最好的一个。 const1 x = 1
是否const1 x = 1
懒惰?
非严格意味着减少(评估的数学术语)从外部进入,
所以如果你有(a +(bc)),那么首先你减少+,然后你减少内部(bc)。
-Haskell维基:Lavy vs非严格
Haskell Wiki真的让我感到困惑。 我明白他们对订单的看法,但我没有看到(a+(b*c))
评价非严格,如果它是通过_|_
?
在非严格评估中,除非函数实际用于评估函数体,否则不会评估函数的参数。
在教会编码下,运营商的懒惰评估映射到功能的非严格评估; 为此,非严格的评估通常被称为“懒惰”。 许多语言中的布尔表达式使用一种称为短路评估的非严格评估形式,一旦可以确定会产生明确的布尔值,评估就会返回 - 例如,在遇到真值的分离表达式中,或者在遇到错误的连接表达式等等。 条件表达式通常也使用懒惰评估,一旦明确的分支结果就会立即返回。
-Wikipedia:评估策略
懒惰Def:
另一方面,懒惰评价意味着只有在需要结果的时候评价表达(注意从“减少”转变为“评价”)。 因此,当评估引擎看到一个表达式时,它会构建一个thunk数据结构,其中包含评估表达式所需的任何值以及指向表达式本身的指针。 当实际需要结果时,评估引擎调用表达式,然后用结果替换thunk以备将来参考。 ...
很明显,在一个部分评估的表达式之间存在强烈的对应关系。 因此在大多数情况下,术语“懒惰”和“非严格”是同义词。 但不完全。
-Haskell维基:Lavy vs非严格
这看起来像一个Haskell特定的答案。 我认为懒惰和非严格意味着部分评价。 这个比较是否简化了? 懒惰总是意味着thunk和non-strict总是意味着部分评价。
在编程语言理论中,懒惰评估或按需呼叫1是评估策略,它延迟表达式的评估直到其实际需要(非严格评估)并且还避免重复评估(共享)。
-Wikipedia:懒惰评估
重要的例子
我知道大多数人在学习功能语言时都会忘记命令式编程。 但是,我想知道这些是非严格的,懒惰的,还是两者都不符合? 至少它会提供一些熟悉的东西。
短路
f1() || f2()
C#,Python和其他语言“yield”
public static IEnumerable Power(int number, int exponent)
{
int counter = 0;
int result = 1;
while (counter++ < exponent)
{
result = result * number;
yield return result;
}
}
-MSDN:yield(c#)
回调
int f1() { return 1;}
int f2() { return 2;}
int lazy(int (*cb1)(), int (*cb2)() , int x) {
if (x == 0)
return cb1();
else
return cb2();
}
int eager(int e1, int e2, int x) {
if (x == 0)
return e1;
else
return e2;
}
lazy(f1, f2, x);
eager(f1(), f2(), x);
问题
我知道所有这些资源在我面前的答案是正确的,但我无法理解。 看起来这个定义太容易被忽视或明显地被驳回。
我知道我有很多问题。 随时回答您认为相关的任何问题。 我添加了这些问题供讨论。
const1 x = 1
也懒吗? const1 x = 1
? 减少似乎符合懒惰的定义。 谢谢你这么!
非严格和懒惰,虽然非正式可互换,适用于不同的讨论领域。
非严格指语义:表达式的数学意义。 非严格适用的世界没有关于功能运行时间,内存消耗甚至计算机的概念。 它只是谈论域中的哪些值映射到共域中的哪些类型的值。 特别是,严格函数必须将值⊥(“bottom” - 参见上面的语义链接以获取更多信息)映射到⊥; 一个非严格的功能是不允许的。
懒惰是指操作行为:代码在真实计算机上执行的方式。 大多数程序员在操作上会考虑程序,所以这可能就是你的想法。 懒惰评估是指使用thunks的实现 - 指向代码的指针,这些指针在第一次执行时被替换为值。 注意这里的非语义词:“指针”,“第一次”,“执行”。
懒惰评估会产生非严格的语义,这就是为什么这些概念看起来如此接近。 但正如FUZxxl指出的那样,懒惰不是实现非严格语义的唯一方法。
如果您有兴趣了解更多关于这个区别的信息,我强烈建议您使用上面的链接。 阅读它是我对计算机程序意义概念的一个转折点。
一个评估模型的例子,既不严格也不懒惰: 乐观评估 ,它提供了一些加速,因为它可以避免很多“简单”的thunk:
乐观的评估意味着,即使一个子表达式可能不需要评估超级表达式,我们仍然使用一些启发式评估其中的一些。 如果子表达式没有足够快地终止,我们会暂停评估,直到真正需要为止。 如果稍后需要子表达式,这给了我们一个优于懒惰评估的优势,因为我们不需要生成一个thunk。 另一方面,如果表达没有终止,我们不会失去太多,因为我们可以很快地放弃它。
正如你所看到的,这个评估模型并不严格:如果产生_ | _的东西被评估,但是不需要,那么当引擎中止评估时,该函数将会终止。 另一方面,可能会有超过需要的表达式被评估,所以它不是完全懒惰的。
是的,这里有一些术语不明确的用法,但是在大多数情况下,术语是一致的,所以它不是太大的问题。
一个主要区别是条件评估。 对此有多种策略,范围从“尽快”到“仅在最后时刻”。 渴望评价一词有时用于倾向于前者的策略,而懒惰评价恰当地指的是倾向于后者的一系列策略。 “懒惰评估”与相关策略之间的区别倾向于涉及何时何地评估某些事物的结果被保留,而不是被抛在一边。 Haskell中熟悉的为数据结构分配名称并将其编入索引的memoization技术就是基于此。 相反,一种简单地将表达互相拼接的语言(如“按名称”评估)可能不支持这一点。
另一个区别是评估哪些术语,从“绝对一切”到“尽可能少”。 由于实际用于计算最终结果的任何值都不能被忽略,因此这里的差异是评估了多少多余的术语。 除了减少程序所要做的工作量外,忽略不使用的术语意味着它们所产生的任何错误都不会发生。 在划分区别时, 严格性指的是评估所考虑事项的性质(在严格的功能的情况下,例如,这意味着它适用的术语,它并不一定意味着参数内的子表达式) 而非严格意味着只评估一些事情(通过延迟评估或完全放弃条款)。
应该很容易看出这些以复杂的方式相互作用; 决定根本不是正交的,因为极端往往是不相容的。 例如:
非严格的评估排除了某种程度的渴望; 如果你不知道是否需要一个术语,那么你还不能评估它。
非常严格的评估使得非热切有点不相关; 如果你正在评估所有事情,那么什么时候做这件事的决定不那么重要。
不过,其他定义确实存在。 例如,至少在Haskell中,“严格函数”通常被定义为一种强制其参数足以使函数评估为| 每当有任何争论时, 请注意,通过这个定义, id
是严格的(在一个微不足道的意义上),因为强制id x
的结果与单独强制x
有完全相同的行为。