seq运算符是
seq :: a -> b -> b
x
seq
y将评估x,足以检查它不是底部,然后 丢弃结果并评估y。这似乎没有用,但它 表示在考虑y之前保证评估x。
这对Haskell来说非常好,但这意味着在
中x `seq` f x
评估x
的费用是否会支付两次(“丢弃结果”)?
答案 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
在任何情况下都不评估x
。seq x (f x)
时,f = id
不评估任何内容,与the report似乎一直在说的相反。
答案 4 :(得分:1)
您可以随时查看unsafePerformIO
或trace
...
import System.IO.Unsafe (unsafePerformIO)
main = print (x `seq` f (x + x))
where
f = (+4)
x = unsafePerformIO $ print "Batman!" >> return 3