12

这个常见问题解答

seq 运算符是

seq :: a -> b -> b

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

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

x `seq` f x

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

4

5 回答 5

18

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

于 2012-03-14T17:54:37.900 回答
12

不,它不是计算和忘记,它是计算——它强制缓存。

例如,考虑以下代码:

 let x = 1 + 1
 in x + 1

由于 Haskell 是惰性的,因此计算结果为((1 + 1) + 1). 一个 thunk,包含一个 thunk 和 1 之和,内部 thunk 是一加一。

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

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

像这样将 thunk 链接在一起可能会导致堆栈溢出,如果重复执行,那么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() );
 }
于 2012-03-14T17:54:07.137 回答
4

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

于 2012-03-14T17:57:52.943 回答
1

您可以随时检查unsafePerformIOtrace...</p>

import System.IO.Unsafe (unsafePerformIO)

main = print (x `seq` f (x + x))
  where
    f = (+4)
    x = unsafePerformIO $ print "Batman!" >> return 3
于 2012-03-15T13:27:46.170 回答
1

当然seq,它本身并不评估”任何东西。它只记录强制顺序依赖。强制本身是由模式匹配触发的。当seq x (f x)被强制时,x将首先被强制(记忆结果值),然后f x将被强制。Haskell 的惰性求值意味着它会记住强制表达式的结果,因此不会执行重复的“求值”(这里是吓人的引号)。

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

“Haskell 值是高度分层的;‘评估’一个 Haskell 值可能意味着评估到这些层中的任何一层。”

让我重申一下:seq它本身并不评估任何东西。 在任何情况下seq x x都不评估。时不评估任何东西,这与报告似乎一直在说的相反。xseq x (f x)f = id

于 2012-03-15T05:04:29.573 回答