Haskell`seq`运算符的时间成本

qua*_*dev 12 haskell strict lazy-evaluation seq

这个FAQ说明了这一点

seq运算符是

seq :: a -> b -> b
Run Code Online (Sandbox Code Playgroud)

x seqy将评估x,足以检查它是否为底部,然后丢弃结果并评估y.这可能看起来没什么用,但这意味着在考虑y之前保证会对x进行求值.

这对Haskell来说非常好,但它是否意味着在

x `seq` f x
Run Code Online (Sandbox Code Playgroud)

评估费用x将支付两次("丢弃结果")?

dfl*_*str 18

seq函数将丢弃该值x,但由于该值已被评估,所有引用x都将"更新",不再指向未x评估的版本,而是指向评估版本.因此,即使seq评估和丢弃x,也已为其他用户评估该值x,导致不重复评估.


ram*_*ion 12

不,它不是计算而忘记,它是计算 - 它强制缓存.

例如,考虑以下代码:

 let x = 1 + 1
 in x + 1
Run Code Online (Sandbox Code Playgroud)

由于Haskell很懒,所以评估为((1 + 1) + 1).一个thunk,包含thunk和one的总和,内部thunk是一加一.

让我们使用非懒惰语言javascript来展示它的样子:

 function(){
   var x = function(){ return 1 + 1 };
   return x() + 1;
 }
Run Code Online (Sandbox Code Playgroud)

像这样链接在一起的thunk可能会导致堆栈溢出,如果反复执行,那么seq救援.

let x = 1 + 1
in x `seq` (x + 1)
Run Code Online (Sandbox Code Playgroud)

当我告诉你这个评价时,我撒谎(2 + 1),但这几乎是正确的 - 只是在剩下的事情发生之前强制发生2的计算(但是仍然懒得计算2).

回到javascript:

 function(){
   var x = function(){ return 1 + 1 };
   return (function(x){
     return x + 1;
   })( x() );
 }
Run Code Online (Sandbox Code Playgroud)