2012-03-14 82 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