This FAQ的时间成本说哈斯克尔`seq`运营商
的序列运算符是
seq :: a -> b -> b
X
seq
Ÿ将评估X,足以确认这是不是底部,然后 丢弃结果并评估y。这可能看起来不太有用,但它 意味着保证x在考虑y之前被评估。
这是非常不错的Haskell的,但它意味着,在
x `seq` f x
评估x
的成本将支付两次(“废弃结果”)?
This FAQ的时间成本说哈斯克尔`seq`运营商
的序列运算符是
seq :: a -> b -> b
X
seq
Ÿ将评估X,足以确认这是不是底部,然后 丢弃结果并评估y。这可能看起来不太有用,但它 意味着保证x在考虑y之前被评估。
这是非常不错的Haskell的,但它意味着,在
x `seq` f x
评估x
的成本将支付两次(“废弃结果”)?
的seq
功能将丢弃的x
价值,但由于价值进行了评估,以x
所有引用“更新”不再指向未评估版本的x
,而是指向评估版本。因此,即使seq
评估并丢弃了x
,该值也已针对x
的其他用户进行了评估,导致不会重复评估。
不,它不是计算和忘记,它是计算 - 它强制高速缓存。
例如,考虑下面的代码:
let x = 1 + 1
in x + 1
由于Haskell是惰性的,这种评估为((1 + 1) + 1)
。一个thunk,包含一个thunk和一个的总和,内部thunk是一个加一。
让我们使用JavaScript,非懒语,以示这是什么样子:
function(){
var x = function(){ return 1 + 1 };
return x() + 1;
}
的thunk seq
串联起来这样才能cause stack overflows, if done repeatedly,使救援。
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());
}
我相信x
只会被评估一次(并且结果保留供将来使用,这是惰性操作的典型特征)。这种行为使得seq
有用。
当然seq
by itself does not "evaluate" anything。它只记录强制顺序依赖关系。强制本身由模式匹配触发。当seq x (f x)
被强制时,x
将被强制首先(记忆结果值),然后f x
将被强制。Haskell的懒惰评估意味着它记录表达式的强迫结果,因此不会重复“评估”(这里是可怕的引号)。
我把“评价”纳入可怕的报价中,因为它暗示完整评价。用Haskell wikibook,
, 的话说:“Haskell值是高度分层的;'评估'Haskell值可能意味着评估这些层中的任何一个。”
让我重申:seq
本身不计算任何东西。seq x x
在任何情况下均不评估x
。 seq x (f x)
在f = id
与the report似乎一直在说的相反时不会评估任何内容。
您可以随时与unsafePerformIO
或trace
检查...
import System.IO.Unsafe (unsafePerformIO)
main = print (x `seq` f (x + x))
where
f = (+4)
x = unsafePerformIO $ print "Batman!" >> return 3
也许 “丢弃的结果” 是太强大了。它以同样的方式丢弃结果,const抛弃它的第二个参数。如果论证已经被评估,它不会以某种方式评估它或丢弃结果,它只是忽略它。 “x'seq'y将评估x,足以检查它不是底部的,然后_ignore_结果并评估y”可能是更好的方式来描述它。 – MatrixFrog 2012-03-15 07:32:33
对我来说,Haskell的计算模型与我的核心编程语言(C++)有很大不同。 – 2012-03-15 09:48:50