2012-03-14 81 views
11

This FAQ的时间成本说哈斯克尔`seq`运营商

的序列运算符是

seq :: a -> b -> b 

X seqŸ将评估X,足以确认这是不是底部,然后 丢弃结果并评估y。这可能看起来不太有用,但它 意味着保证x在考虑y之前被评估。

这是非常不错的Haskell的,但它意味着,在

x `seq` f x 

评估x的成本将支付两次(“废弃结果”)?

+0

也许 “丢弃的结果” 是太强大了。它以同样的方式丢弃结果,const抛弃它的第二个参数。如果论证已经被评估,它不会以某种方式评估它或丢弃结果,它只是忽略它。 “x'seq'y将评估x,足以检查它不是底部的,然后_ignore_结果并评估y”可能是更好的方式来描述它。 – MatrixFrog 2012-03-15 07:32:33

+0

对我来说,Haskell的计算模型与我的核心编程语言(C++)有很大不同。 – 2012-03-15 09:48:50

回答

17

seq功能将丢弃的x价值,但由于价值进行了评估,以x所有引用“更新”不再指向未评估版本的x,而是指向评估版本。因此,即使seq评估并丢弃了x,该值也已针对x的其他用户进行了评估,导致不会重复评估。

12

不,它不是计算和忘记,它是计算 - 它强制高速缓存。

例如,考虑下面的代码:

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()); 
} 
4

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

1

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

我把“评价”纳入可怕的报价中,因为它暗示完整评价。用Haskell wikibook,

, 的话说:“Haskell值是高度分层的;'评估'Haskell值可能意味着评估这些层中的任何一个。”

让我重申:seq本身不计算任何东西。seq x x在任何情况下均不评估xseq x (f x)f = idthe report似乎一直在说的相反时不会评估任何内容。

1

您可以随时与unsafePerformIOtrace检查...

import System.IO.Unsafe (unsafePerformIO) 

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