如何在恒定空间中使用单子(monad) Action 折叠懒惰列表?我要解决的问题是聚合一个大文件,并且我相信出于性能考虑,我需要可变性。我在ST中使用可变向量来实现一个实现,但是它使用了过多的内存。以下是我正在尝试的示例。我还对Conduit进行了简短的实验,但这似乎没有任何改善。
ST forM_:
import Control.Monad (forM_)
import Control.Monad.ST.Trans as STT
import Control.Monad.Identity as Identity
testST :: Int
testST = do
Identity.runIdentity $ STT.runST $ do
a <- STT.newSTRef 0
forM_ [1..10000000] (\x -> do
a' <- STT.readSTRef a
STT.writeSTRef a (a' + x)
)
STT.readSTRef a
导管:
import Data.Conduit (($=),(=$),($$))
import qualified Data.Conduit as C
import qualified Data.Conduit.List as CL
testCL :: IO Int
testCL = CL.sourceList [1..10000000] $$ CL.foldM (\a x -> return (a + x)) 0
最佳答案
问题不在于褶皱,而在于褶皱主体。这个程序分配了很多:
testST = runST $ do
ref <- newSTRef 0
forM_ [1..10000000] $ \x -> do
val <- readSTRef ref
writeSTRef ref (val + x)
readSTRef ref
该程序的唯一区别是
writeSTRef
行,它几乎不分配任何内容:testST = runST $ do
ref <- newSTRef 0
forM_ [1..10000000] $ \x -> do
val <- readSTRef ref
writeSTRef ref $! val + x
readSTRef ref
这两段代码之间的区别很好地说明了正在发生的事情:在前一种情况下,您将创建一个对深度嵌套的thunk的引用,该thunk具有10000000层
+
应用程序;而后者在每一步都压扁了重击。顺便说一句,这个常见的陷阱是explicitly called out in the documentation for
modifySTRef
。