如何在恒定空间中使用monadic动作折叠惰性列表?我试图解决的问题是聚合一个大文件,我相信为了性能我需要可变性。我有一个使用可变向量在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
答案 0 :(得分:15)
问题不在于折叠,而在于折叠体。这个程序分配了很多:
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
两段代码之间的差异是对正在发生的事情的一个很好的暗示:在前者中,您正在创建对具有10000000层+
应用程序的深层嵌套thunk的引用;而后者在每一步都会使thunk变平。
顺便说一下,这个常见的陷阱是explicitly called out in the documentation for modifySTRef
。