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

时间:2012-03-14 17:44:34

标签: haskell lazy-evaluation strict seq

This FAQ

  

seq运算符是

seq :: a -> b -> b
     

x seq y将评估x,足以检查它不是底部,然后   丢弃结果并评估y。这似乎没有用,但它   表示在考虑y之前保证评估x。

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

x `seq` f x

评估x的费用是否会支付两次(“丢弃结果”)?

5 个答案:

答案 0 :(得分:18)

seq函数将丢弃x的值,但由于已经评估了该值,因此对x的所有引用都“更新”,不再指向未评估的版本x,而是指向评估版本。因此,即使seq评估并放弃x,也会为x的其他用户评估该值,从而导致无法重复评估。

答案 1 :(得分:12)

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

例如,请考虑以下代码:

 let x = 1 + 1
 in x + 1

由于Haskell是惰性的,因此评估为((1 + 1) + 1)。一个thunk,包含thunk和one的总和,内部thunk是一加一。

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

 function(){
   var x = function(){ return 1 + 1 };
   return x() + 1;
 }

将这样的thunk链接在一起可以cause stack overflows, if done repeatedly,所以seq可以拯救。

let x = 1 + 1
in x `seq` (x + 1)

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

回到javascript:

 function(){
   var x = function(){ return 1 + 1 };
   return (function(x){
     return x + 1;
   })( x() );
 }

答案 2 :(得分:4)

我相信x只会被评估一次(并且结果会保留供将来使用,这对于延迟操作来说是典型的)。这种行为使seq变得有用。

答案 3 :(得分:1)

当然seq by itself does not "evaluate" anything。它只记录强制顺序依赖。强制本身由模式匹配触发。强制seq x (f x)时,将强制x(记住结果值),然后强制f x。 Haskell的懒惰评估意味着它会记住强制表达式的结果,因此不会重复执行“评估”(这里有可怕的引用)。

我将“评估”放入可怕的引号中,因为它意味着完整评估。用Haskell wikibook的话来说,

“Haskell值是高度分层的;'评估'Haskell值可能意味着评估这些层中的任何一个。” < / blockquote>

让我重申一下:seq本身并不评估任何内容。 seq x x在任何情况下都不评估xseq x (f x)时,f = id不评估任何内容,与the report似乎一直在说的相反。

答案 4 :(得分:1)

您可以随时查看unsafePerformIOtrace ...

import System.IO.Unsafe (unsafePerformIO)

main = print (x `seq` f (x + x))
  where
    f = (+4)
    x = unsafePerformIO $ print "Batman!" >> return 3