[英]Understanding Haskell seq
我是一個Haskell新手,在理解seq
遇到了問題,我在下面的例子中對此進行了總結。
以下是相同(愚蠢)函數的2個實現。 該函數采用正int n並返回元組(n,n)
。 通過使用具有元組累加器的輔助函數從(0,0)
向上計數來產生答案。 為了避免構建一個龐大的thunk我使用seq來使累加器嚴格。
在testSeq1
中,元組的內容是嚴格的,而在testSeq2
,元組本身是嚴格的。 我認為這兩個實現將執行相同的操作。 但實際上,'使用中的總內存'對於testSeq1
僅為1MB,而對於testSeq2則為testSeq2
(當使用n = 1000000進行測試時)。
testSeq2
什么問題?
testSeq1 :: Int -> (Int,Int)
testSeq1 n = impl n (0,0) where
impl 0 (a,b) = (a,b)
impl i (a,b) = seq a $ seq b $ impl (i-1) (a+1, b+1)
testSeq2 :: Int -> (Int,Int)
testSeq2 n = impl n (0,0) where
impl 0 acc = acc
impl i acc = seq acc $ impl (i-1) ((fst acc)+1, (snd acc)+1)
seq
ing一個元組只會強制它被評估,以暴露它的元組構造函數,但不會評估它的組件。
那是,
let pair = id (1+1, 2+2)
in seq pair $ ...
將應用id
並生成(_thunk1, _thunk2)
,其中_thunk
指向添加,此時不進行評估。
在你的第二個例子中,你強制累加器acc
,而不是它的組件,因此仍然會積累一些大的thunk。
您可以使用所謂的評估策略,例如:
evalPair :: (a,b) -> ()
evalPair (a,b) = seq a $ seq b ()
testSeq2 :: Int -> (Int,Int)
testSeq2 n = impl n (0,0) where
impl 0 acc = acc
impl i acc = seq (evalPair acc) $ impl (i-1) ((fst acc)+1, (snd acc)+1)
但是, testSeq1
是一種更簡單的方法。
作為另一種選擇,使用嚴格的元組 。 這樣的元組永遠不會有組件的thunk,但只評估結果。 強制tuple構造函數將強制組件,如您所料。
import Data.Strict.Tuple
testSeq2 :: Int -> Pair Int Int
testSeq2 n = impl n (0 :!: 0) where
impl 0 acc = acc
impl i acc = seq acc $ impl (i-1) ((fst acc + 1) :!: (snd acc + 1))
seq
只強制對其第一個參數進行淺層評估。 你可以看到這兩個例子:
errorTuple :: (Int, Int)
errorTuple = undefined
errorTupleContents :: (Int, Int)
errorTupleContents = (undefined, undefined)
case1 = errorTuple `seq` (1, 1)
case2 = errorTupleContents `seq` (1, 1)
case1
將因undefined
錯誤而失敗,因為seq
試圖強制評估errorTuple
,這是undefined
,但是, case2
不會,因為元組構造函數被計算並返回其內容未被評估的元組。 如果他們被評估,他們將是undefined
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.