2015-09-21 10 views
9

定数空間でのモナドアクションを使用してレイジーリストを折り畳むにはどうすればよいですか?私が解決しようとしている問題は、大きなファイルを集約することです。パフォーマンスのために私は変更可能性が必要だと考えています。私は変更可能なベクトルを使用してSTで動作する実装を持っていますが、あまりにも多くのメモリを使います。以下は、私が試みていることの例です。私もコンジットで簡単に実験しましたが、改善は見られませんでした。定数空間のMonadic Fold

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

パフォーマンスチューニングのために: 'STT s Identity'は通常の' ST s'よりいくらかの割り当てオーバーヘッドを持つように見えます。独特の 'STT'力が必要ない場合は' ST'を使うだけです。 – dfeuer

+0

@dfeuer確かに私はそれを削除するかもしれませんが、私はそれを最初に 'Either'を埋め込む必要があると思って実装に入れ、それが転送されました。ヒントをありがとう! – ryachza

答えて

15

問題は倍ではなく、倍体ではありません。このプログラムは、多くの割り当て:

testST = runST $ do 
    ref <- newSTRef 0 
    forM_ [1..10000000] $ \x -> do 
     val <- readSTRef ref 
     writeSTRef ref $! val + x 
    readSTRef ref 

コードの2枚の違いは何に良いヒントがある:

testST = runST $ do 
    ref <- newSTRef 0 
    forM_ [1..10000000] $ \x -> do 
     val <- readSTRef ref 
     writeSTRef ref (val + x) 
    readSTRef ref 

このプログラム、その唯一の違いwriteSTRefライン上にあるが、ほとんど何も割り当てません進んでいる:前者では、+の10000000層のアプリケーションを持つ深くネストされたサンクへの参照を作成しています。後者は各ステップでサンクを平らにする。

ところで、この共通の落とし穴はexplicitly called out in the documentation for modifySTRefです。

+0

これは完璧に機能しました。私はいくつかの厳密な注釈と異なる折り畳み方法を試しましたが、私の焦点は常にアプリケーションではなく議論に焦点を当てていました。それが書き込み価値であるという考えは決して私の心を越えたことはありません。 – ryachza

+0

@ryachza確かに、それは深夜、最後の分のデバッグセッションの絶望によって私の脳の中に焼かれた微妙なエラーです... –