繁体   English   中英

理解Haskell seq

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM